/src/nss-nspr/nss/lib/dev/devslot.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #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 | 0 | { |
34 | 0 | if (slot) { |
35 | 0 | if (PR_ATOMIC_DECREMENT(&slot->base.refCount) == 0) { |
36 | 0 | PK11_FreeSlot(slot->pk11slot); |
37 | 0 | PZ_DestroyLock(slot->base.lock); |
38 | 0 | PZ_DestroyCondVar(slot->isPresentCondition); |
39 | 0 | PZ_DestroyLock(slot->isPresentLock); |
40 | 0 | return nssArena_Destroy(slot->base.arena); |
41 | 0 | } |
42 | 0 | } |
43 | 0 | return PR_SUCCESS; |
44 | 0 | } |
45 | | |
46 | | void |
47 | | nssSlot_EnterMonitor(NSSSlot *slot) |
48 | 0 | { |
49 | 0 | if (slot->lock) { |
50 | 0 | PZ_Lock(slot->lock); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | void |
55 | | nssSlot_ExitMonitor(NSSSlot *slot) |
56 | 0 | { |
57 | 0 | if (slot->lock) { |
58 | 0 | PZ_Unlock(slot->lock); |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | NSS_IMPLEMENT void |
63 | | NSSSlot_Destroy( |
64 | | NSSSlot *slot) |
65 | 0 | { |
66 | 0 | (void)nssSlot_Destroy(slot); |
67 | 0 | } |
68 | | |
69 | | NSS_IMPLEMENT NSSSlot * |
70 | | nssSlot_AddRef( |
71 | | NSSSlot *slot) |
72 | 0 | { |
73 | 0 | PR_ATOMIC_INCREMENT(&slot->base.refCount); |
74 | 0 | return slot; |
75 | 0 | } |
76 | | |
77 | | NSS_IMPLEMENT NSSUTF8 * |
78 | | nssSlot_GetName( |
79 | | NSSSlot *slot) |
80 | 0 | { |
81 | 0 | return slot->base.name; |
82 | 0 | } |
83 | | |
84 | | NSS_IMPLEMENT void |
85 | | nssSlot_ResetDelay( |
86 | | NSSSlot *slot) |
87 | 0 | { |
88 | 0 | PZ_Lock(slot->isPresentLock); |
89 | 0 | slot->lastTokenPingState = nssSlotLastPingState_Reset; |
90 | 0 | PZ_Unlock(slot->isPresentLock); |
91 | 0 | } |
92 | | |
93 | | static PRBool |
94 | | token_status_checked(const NSSSlot *slot) |
95 | 0 | { |
96 | 0 | PRIntervalTime time; |
97 | 0 | 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 | 0 | if (slot->isPresentThread == PR_GetCurrentThread()) { |
102 | 0 | return PR_TRUE; |
103 | 0 | } |
104 | | /* Set the delay time for checking the token presence */ |
105 | 0 | if (s_token_delay_time == 0) { |
106 | 0 | s_token_delay_time = PR_SecondsToInterval(NSSSLOT_TOKEN_DELAY_TIME); |
107 | 0 | } |
108 | 0 | time = PR_IntervalNow(); |
109 | 0 | if ((lastPingState == nssSlotLastPingState_Valid) && ((time - slot->lastTokenPingTime) < s_token_delay_time)) { |
110 | 0 | return PR_TRUE; |
111 | 0 | } |
112 | 0 | return PR_FALSE; |
113 | 0 | } |
114 | | |
115 | | NSS_IMPLEMENT PRBool |
116 | | nssSlot_IsTokenPresent( |
117 | | NSSSlot *slot) |
118 | 0 | { |
119 | 0 | CK_RV ckrv; |
120 | 0 | PRStatus nssrv; |
121 | 0 | NSSToken *nssToken = NULL; |
122 | | /* XXX */ |
123 | 0 | nssSession *session; |
124 | 0 | CK_SLOT_INFO slotInfo; |
125 | 0 | void *epv; |
126 | 0 | PRBool isPresent = PR_FALSE; |
127 | 0 | PRBool doUpdateCachedCerts = PR_FALSE; |
128 | | |
129 | | /* permanent slots are always present unless they're disabled */ |
130 | 0 | if (nssSlot_IsPermanent(slot)) { |
131 | 0 | return !PK11_IsDisabled(slot->pk11slot); |
132 | 0 | } |
133 | | |
134 | | /* avoid repeated calls to check token status within set interval */ |
135 | 0 | PZ_Lock(slot->isPresentLock); |
136 | 0 | if (token_status_checked(slot)) { |
137 | 0 | CK_FLAGS ckFlags = slot->ckFlags; |
138 | 0 | PZ_Unlock(slot->isPresentLock); |
139 | 0 | return ((ckFlags & CKF_TOKEN_PRESENT) != 0); |
140 | 0 | } |
141 | 0 | 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 | 0 | epv = slot->epv; |
146 | 0 | if (!epv) { |
147 | 0 | return PR_FALSE; |
148 | 0 | } |
149 | | |
150 | | /* set up condition so only one thread is active in this part of the code at a time */ |
151 | 0 | PZ_Lock(slot->isPresentLock); |
152 | 0 | while (slot->isPresentThread) { |
153 | 0 | PR_WaitCondVar(slot->isPresentCondition, PR_INTERVAL_NO_TIMEOUT); |
154 | 0 | } |
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 | 0 | if (token_status_checked(slot)) { |
158 | 0 | CK_FLAGS ckFlags = slot->ckFlags; |
159 | 0 | PZ_Unlock(slot->isPresentLock); |
160 | 0 | return ((ckFlags & CKF_TOKEN_PRESENT) != 0); |
161 | 0 | } |
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 | 0 | slot->lastTokenPingState = nssSlotLastPingState_Update; |
165 | 0 | slot->isPresentThread = PR_GetCurrentThread(); |
166 | |
|
167 | 0 | PZ_Unlock(slot->isPresentLock); |
168 | |
|
169 | 0 | nssToken = PK11Slot_GetNSSToken(slot->pk11slot); |
170 | 0 | if (!nssToken) { |
171 | 0 | isPresent = PR_FALSE; |
172 | 0 | goto done; |
173 | 0 | } |
174 | | |
175 | 0 | if (PK11_GetSlotInfo(slot->pk11slot, &slotInfo) != SECSuccess) { |
176 | 0 | nssToken->base.name[0] = 0; /* XXX */ |
177 | 0 | isPresent = PR_FALSE; |
178 | 0 | goto done; |
179 | 0 | } |
180 | 0 | slot->ckFlags = slotInfo.flags; |
181 | | /* check for the presence of the token */ |
182 | 0 | if ((slot->ckFlags & CKF_TOKEN_PRESENT) == 0) { |
183 | 0 | session = nssToken_GetDefaultSession(nssToken); |
184 | 0 | if (session) { |
185 | 0 | nssSession_EnterMonitor(session); |
186 | | /* token is not present */ |
187 | 0 | if (session->handle != CK_INVALID_HANDLE) { |
188 | | /* session is valid, close and invalidate it */ |
189 | 0 | CKAPI(epv) |
190 | 0 | ->C_CloseSession(session->handle); |
191 | 0 | session->handle = CK_INVALID_HANDLE; |
192 | 0 | } |
193 | 0 | nssSession_ExitMonitor(session); |
194 | 0 | } |
195 | 0 | if (nssToken->base.name[0] != 0) { |
196 | | /* notify the high-level cache that the token is removed */ |
197 | 0 | nssToken->base.name[0] = 0; /* XXX */ |
198 | 0 | nssToken_NotifyCertsNotVisible(nssToken); |
199 | 0 | } |
200 | 0 | nssToken->base.name[0] = 0; /* XXX */ |
201 | | /* clear the token cache */ |
202 | 0 | nssToken_Remove(nssToken); |
203 | 0 | isPresent = PR_FALSE; |
204 | 0 | goto done; |
205 | 0 | } |
206 | | /* token is present, use the session info to determine if the card |
207 | | * has been removed and reinserted. |
208 | | */ |
209 | 0 | session = nssToken_GetDefaultSession(nssToken); |
210 | 0 | if (session) { |
211 | 0 | PRBool tokenRemoved; |
212 | 0 | nssSession_EnterMonitor(session); |
213 | 0 | if (session->handle != CK_INVALID_HANDLE) { |
214 | 0 | CK_SESSION_INFO sessionInfo; |
215 | 0 | ckrv = CKAPI(epv)->C_GetSessionInfo(session->handle, &sessionInfo); |
216 | 0 | if (ckrv != CKR_OK) { |
217 | | /* session is screwy, close and invalidate it */ |
218 | 0 | CKAPI(epv) |
219 | 0 | ->C_CloseSession(session->handle); |
220 | 0 | session->handle = CK_INVALID_HANDLE; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | tokenRemoved = (session->handle == CK_INVALID_HANDLE); |
224 | 0 | nssSession_ExitMonitor(session); |
225 | | /* token not removed, finished */ |
226 | 0 | if (!tokenRemoved) { |
227 | 0 | isPresent = PR_TRUE; |
228 | 0 | goto done; |
229 | 0 | } |
230 | 0 | } |
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 | 0 | nssToken_NotifyCertsNotVisible(nssToken); |
236 | 0 | nssToken_Remove(nssToken); |
237 | 0 | if (nssToken->base.name[0] == 0) { |
238 | 0 | doUpdateCachedCerts = PR_TRUE; |
239 | 0 | } |
240 | 0 | if (PK11_InitToken(slot->pk11slot, PR_FALSE) != SECSuccess) { |
241 | 0 | isPresent = PR_FALSE; |
242 | 0 | goto done; |
243 | 0 | } |
244 | 0 | if (doUpdateCachedCerts) { |
245 | 0 | nssTrustDomain_UpdateCachedTokenCerts(nssToken->trustDomain, |
246 | 0 | nssToken); |
247 | 0 | } |
248 | 0 | nssrv = nssToken_Refresh(nssToken); |
249 | 0 | if (nssrv != PR_SUCCESS) { |
250 | 0 | nssToken->base.name[0] = 0; /* XXX */ |
251 | 0 | slot->ckFlags &= ~CKF_TOKEN_PRESENT; |
252 | 0 | isPresent = PR_FALSE; |
253 | 0 | goto done; |
254 | 0 | } |
255 | 0 | isPresent = PR_TRUE; |
256 | 0 | done: |
257 | 0 | if (nssToken) { |
258 | 0 | (void)nssToken_Destroy(nssToken); |
259 | 0 | } |
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 | 0 | PZ_Lock(slot->isPresentLock); |
269 | | /* don't update the time if we were reset while we were |
270 | | * getting the token state */ |
271 | 0 | if (slot->lastTokenPingState == nssSlotLastPingState_Update) { |
272 | 0 | slot->lastTokenPingTime = PR_IntervalNow(); |
273 | 0 | slot->lastTokenPingState = nssSlotLastPingState_Valid; |
274 | 0 | } |
275 | 0 | slot->isPresentThread = NULL; |
276 | 0 | PR_NotifyAllCondVar(slot->isPresentCondition); |
277 | 0 | PZ_Unlock(slot->isPresentLock); |
278 | 0 | return isPresent; |
279 | 0 | } |
280 | | |
281 | | NSS_IMPLEMENT void * |
282 | | nssSlot_GetCryptokiEPV( |
283 | | NSSSlot *slot) |
284 | 0 | { |
285 | 0 | return slot->epv; |
286 | 0 | } |
287 | | |
288 | | NSS_IMPLEMENT NSSToken * |
289 | | nssSlot_GetToken( |
290 | | NSSSlot *slot) |
291 | 0 | { |
292 | 0 | NSSToken *rvToken = NULL; |
293 | |
|
294 | 0 | if (nssSlot_IsTokenPresent(slot)) { |
295 | 0 | rvToken = PK11Slot_GetNSSToken(slot->pk11slot); |
296 | 0 | } |
297 | |
|
298 | 0 | return rvToken; |
299 | 0 | } |
300 | | |
301 | | NSS_IMPLEMENT PRStatus |
302 | | nssSession_EnterMonitor( |
303 | | nssSession *s) |
304 | 0 | { |
305 | 0 | if (s->lock) |
306 | 0 | PZ_Lock(s->lock); |
307 | 0 | return PR_SUCCESS; |
308 | 0 | } |
309 | | |
310 | | NSS_IMPLEMENT PRStatus |
311 | | nssSession_ExitMonitor( |
312 | | nssSession *s) |
313 | 0 | { |
314 | 0 | return (s->lock) ? PZ_Unlock(s->lock) : PR_SUCCESS; |
315 | 0 | } |
316 | | |
317 | | NSS_EXTERN PRBool |
318 | | nssSession_IsReadWrite( |
319 | | nssSession *s) |
320 | 0 | { |
321 | 0 | return s->isRW; |
322 | 0 | } |