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