/src/mozilla-central/security/nss/lib/pk11wrap/pk11auth.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 | | * This file deals with PKCS #11 passwords and authentication. |
6 | | */ |
7 | | #include "seccomon.h" |
8 | | #include "secmod.h" |
9 | | #include "secmodi.h" |
10 | | #include "secmodti.h" |
11 | | #include "pkcs11t.h" |
12 | | #include "pk11func.h" |
13 | | #include "secitem.h" |
14 | | #include "secerr.h" |
15 | | |
16 | | #include "pkim.h" |
17 | | |
18 | | /************************************************************* |
19 | | * local static and global data |
20 | | *************************************************************/ |
21 | | /* |
22 | | * This structure keeps track of status that spans all the Slots. |
23 | | * NOTE: This is a global data structure. It semantics expect thread crosstalk |
24 | | * be very careful when you see it used. |
25 | | * It's major purpose in life is to allow the user to log in one PER |
26 | | * Tranaction, even if a transaction spans threads. The problem is the user |
27 | | * may have to enter a password one just to be able to look at the |
28 | | * personalities/certificates (s)he can use. Then if Auth every is one, they |
29 | | * may have to enter the password again to use the card. See PK11_StartTransac |
30 | | * and PK11_EndTransaction. |
31 | | */ |
32 | | static struct PK11GlobalStruct { |
33 | | int transaction; |
34 | | PRBool inTransaction; |
35 | | char *(PR_CALLBACK *getPass)(PK11SlotInfo *, PRBool, void *); |
36 | | PRBool(PR_CALLBACK *verifyPass)(PK11SlotInfo *, void *); |
37 | | PRBool(PR_CALLBACK *isLoggedIn)(PK11SlotInfo *, void *); |
38 | | } PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL }; |
39 | | |
40 | | /*********************************************************** |
41 | | * Password Utilities |
42 | | ***********************************************************/ |
43 | | /* |
44 | | * Check the user's password. Log into the card if it's correct. |
45 | | * succeed if the user is already logged in. |
46 | | */ |
47 | | static SECStatus |
48 | | pk11_CheckPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, |
49 | | char *pw, PRBool alreadyLocked, PRBool contextSpecific) |
50 | 0 | { |
51 | 0 | int len = 0; |
52 | 0 | CK_RV crv; |
53 | 0 | SECStatus rv; |
54 | 0 | PRTime currtime = PR_Now(); |
55 | 0 | PRBool mustRetry; |
56 | 0 | int retry = 0; |
57 | 0 |
|
58 | 0 | if (slot->protectedAuthPath) { |
59 | 0 | len = 0; |
60 | 0 | pw = NULL; |
61 | 0 | } else if (pw == NULL) { |
62 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
63 | 0 | return SECFailure; |
64 | 0 | } else { |
65 | 0 | len = PORT_Strlen(pw); |
66 | 0 | } |
67 | 0 |
|
68 | 0 | do { |
69 | 0 | if (!alreadyLocked) |
70 | 0 | PK11_EnterSlotMonitor(slot); |
71 | 0 | crv = PK11_GETTAB(slot)->C_Login(session, |
72 | 0 | contextSpecific ? CKU_CONTEXT_SPECIFIC : CKU_USER, |
73 | 0 | (unsigned char *)pw, len); |
74 | 0 | slot->lastLoginCheck = 0; |
75 | 0 | mustRetry = PR_FALSE; |
76 | 0 | if (!alreadyLocked) |
77 | 0 | PK11_ExitSlotMonitor(slot); |
78 | 0 | switch (crv) { |
79 | 0 | /* if we're already logged in, we're good to go */ |
80 | 0 | case CKR_OK: |
81 | 0 | /* TODO If it was for CKU_CONTEXT_SPECIFIC should we do this */ |
82 | 0 | slot->authTransact = PK11_Global.transaction; |
83 | 0 | /* Fall through */ |
84 | 0 | case CKR_USER_ALREADY_LOGGED_IN: |
85 | 0 | slot->authTime = currtime; |
86 | 0 | rv = SECSuccess; |
87 | 0 | break; |
88 | 0 | case CKR_PIN_INCORRECT: |
89 | 0 | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
90 | 0 | rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
91 | 0 | break; |
92 | 0 | /* someone called reset while we fetched the password, try again once |
93 | 0 | * if the token is still there. */ |
94 | 0 | case CKR_SESSION_HANDLE_INVALID: |
95 | 0 | case CKR_SESSION_CLOSED: |
96 | 0 | if (session != slot->session) { |
97 | 0 | /* don't bother retrying, we were in a middle of an operation, |
98 | 0 | * which is now lost. Just fail. */ |
99 | 0 | PORT_SetError(PK11_MapError(crv)); |
100 | 0 | rv = SECFailure; |
101 | 0 | break; |
102 | 0 | } |
103 | 0 | if (retry++ == 0) { |
104 | 0 | rv = PK11_InitToken(slot, PR_FALSE); |
105 | 0 | if (rv == SECSuccess) { |
106 | 0 | if (slot->session != CK_INVALID_SESSION) { |
107 | 0 | session = slot->session; /* we should have |
108 | 0 | * a new session now */ |
109 | 0 | mustRetry = PR_TRUE; |
110 | 0 | } else { |
111 | 0 | PORT_SetError(PK11_MapError(crv)); |
112 | 0 | rv = SECFailure; |
113 | 0 | } |
114 | 0 | } |
115 | 0 | break; |
116 | 0 | } |
117 | 0 | /* Fall through */ |
118 | 0 | default: |
119 | 0 | PORT_SetError(PK11_MapError(crv)); |
120 | 0 | rv = SECFailure; /* some failure we can't fix by retrying */ |
121 | 0 | } |
122 | 0 | } while (mustRetry); |
123 | 0 | return rv; |
124 | 0 | } |
125 | | |
126 | | /* |
127 | | * Check the user's password. Logout before hand to make sure that |
128 | | * we are really checking the password. |
129 | | */ |
130 | | SECStatus |
131 | | PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw) |
132 | 0 | { |
133 | 0 | int len = 0; |
134 | 0 | CK_RV crv; |
135 | 0 | SECStatus rv; |
136 | 0 | PRTime currtime = PR_Now(); |
137 | 0 |
|
138 | 0 | if (slot->protectedAuthPath) { |
139 | 0 | len = 0; |
140 | 0 | pw = NULL; |
141 | 0 | } else if (pw == NULL) { |
142 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
143 | 0 | return SECFailure; |
144 | 0 | } else { |
145 | 0 | len = PORT_Strlen(pw); |
146 | 0 | } |
147 | 0 |
|
148 | 0 | /* |
149 | 0 | * If the token doesn't need a login, don't try to relogin because the |
150 | 0 | * effect is undefined. It's not clear what it means to check a non-empty |
151 | 0 | * password with such a token, so treat that as an error. |
152 | 0 | */ |
153 | 0 | if (!slot->needLogin) { |
154 | 0 | if (len == 0) { |
155 | 0 | rv = SECSuccess; |
156 | 0 | } else { |
157 | 0 | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
158 | 0 | rv = SECFailure; |
159 | 0 | } |
160 | 0 | return rv; |
161 | 0 | } |
162 | 0 |
|
163 | 0 | /* force a logout */ |
164 | 0 | PK11_EnterSlotMonitor(slot); |
165 | 0 | PK11_GETTAB(slot)->C_Logout(slot->session); |
166 | 0 |
|
167 | 0 | crv = PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER, |
168 | 0 | (unsigned char *)pw, len); |
169 | 0 | slot->lastLoginCheck = 0; |
170 | 0 | PK11_ExitSlotMonitor(slot); |
171 | 0 | switch (crv) { |
172 | 0 | /* if we're already logged in, we're good to go */ |
173 | 0 | case CKR_OK: |
174 | 0 | slot->authTransact = PK11_Global.transaction; |
175 | 0 | slot->authTime = currtime; |
176 | 0 | rv = SECSuccess; |
177 | 0 | break; |
178 | 0 | case CKR_PIN_INCORRECT: |
179 | 0 | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
180 | 0 | rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
181 | 0 | break; |
182 | 0 | default: |
183 | 0 | PORT_SetError(PK11_MapError(crv)); |
184 | 0 | rv = SECFailure; /* some failure we can't fix by retrying */ |
185 | 0 | } |
186 | 0 | return rv; |
187 | 0 | } |
188 | | |
189 | | SECStatus |
190 | | PK11_Logout(PK11SlotInfo *slot) |
191 | 0 | { |
192 | 0 | CK_RV crv; |
193 | 0 |
|
194 | 0 | /* force a logout */ |
195 | 0 | PK11_EnterSlotMonitor(slot); |
196 | 0 | crv = PK11_GETTAB(slot)->C_Logout(slot->session); |
197 | 0 | slot->lastLoginCheck = 0; |
198 | 0 | PK11_ExitSlotMonitor(slot); |
199 | 0 | if (crv != CKR_OK) { |
200 | 0 | PORT_SetError(PK11_MapError(crv)); |
201 | 0 | return SECFailure; |
202 | 0 | } |
203 | 0 | return SECSuccess; |
204 | 0 | } |
205 | | |
206 | | /* |
207 | | * transaction stuff is for when we test for the need to do every |
208 | | * time auth to see if we already did it for this slot/transaction |
209 | | */ |
210 | | void |
211 | | PK11_StartAuthTransaction(void) |
212 | 0 | { |
213 | 0 | PK11_Global.transaction++; |
214 | 0 | PK11_Global.inTransaction = PR_TRUE; |
215 | 0 | } |
216 | | |
217 | | void |
218 | | PK11_EndAuthTransaction(void) |
219 | 0 | { |
220 | 0 | PK11_Global.transaction++; |
221 | 0 | PK11_Global.inTransaction = PR_FALSE; |
222 | 0 | } |
223 | | |
224 | | /* |
225 | | * before we do a private key op, we check to see if we |
226 | | * need to reauthenticate. |
227 | | */ |
228 | | void |
229 | | PK11_HandlePasswordCheck(PK11SlotInfo *slot, void *wincx) |
230 | 0 | { |
231 | 0 | int askpw = slot->askpw; |
232 | 0 | PRBool NeedAuth = PR_FALSE; |
233 | 0 |
|
234 | 0 | if (!slot->needLogin) |
235 | 0 | return; |
236 | 0 | |
237 | 0 | if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
238 | 0 | PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
239 | 0 |
|
240 | 0 | if (def_slot) { |
241 | 0 | askpw = def_slot->askpw; |
242 | 0 | PK11_FreeSlot(def_slot); |
243 | 0 | } |
244 | 0 | } |
245 | 0 |
|
246 | 0 | /* timeouts are handled by isLoggedIn */ |
247 | 0 | if (!PK11_IsLoggedIn(slot, wincx)) { |
248 | 0 | NeedAuth = PR_TRUE; |
249 | 0 | } else if (askpw == -1) { |
250 | 0 | if (!PK11_Global.inTransaction || |
251 | 0 | (PK11_Global.transaction != slot->authTransact)) { |
252 | 0 | PK11_EnterSlotMonitor(slot); |
253 | 0 | PK11_GETTAB(slot)->C_Logout(slot->session); |
254 | 0 | slot->lastLoginCheck = 0; |
255 | 0 | PK11_ExitSlotMonitor(slot); |
256 | 0 | NeedAuth = PR_TRUE; |
257 | 0 | } |
258 | 0 | } |
259 | 0 | if (NeedAuth) |
260 | 0 | PK11_DoPassword(slot, slot->session, PR_TRUE, |
261 | 0 | wincx, PR_FALSE, PR_FALSE); |
262 | 0 | } |
263 | | |
264 | | void |
265 | | PK11_SlotDBUpdate(PK11SlotInfo *slot) |
266 | 0 | { |
267 | 0 | SECMOD_UpdateModule(slot->module); |
268 | 0 | } |
269 | | |
270 | | /* |
271 | | * set new askpw and timeout values |
272 | | */ |
273 | | void |
274 | | PK11_SetSlotPWValues(PK11SlotInfo *slot, int askpw, int timeout) |
275 | 0 | { |
276 | 0 | slot->askpw = askpw; |
277 | 0 | slot->timeout = timeout; |
278 | 0 | slot->defaultFlags |= PK11_OWN_PW_DEFAULTS; |
279 | 0 | PK11_SlotDBUpdate(slot); |
280 | 0 | } |
281 | | |
282 | | /* |
283 | | * Get the askpw and timeout values for this slot |
284 | | */ |
285 | | void |
286 | | PK11_GetSlotPWValues(PK11SlotInfo *slot, int *askpw, int *timeout) |
287 | 0 | { |
288 | 0 | *askpw = slot->askpw; |
289 | 0 | *timeout = slot->timeout; |
290 | 0 |
|
291 | 0 | if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
292 | 0 | PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
293 | 0 |
|
294 | 0 | if (def_slot) { |
295 | 0 | *askpw = def_slot->askpw; |
296 | 0 | *timeout = def_slot->timeout; |
297 | 0 | PK11_FreeSlot(def_slot); |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | /* |
303 | | * Returns true if the token is needLogin and isn't logged in. |
304 | | * This function is used to determine if authentication is needed |
305 | | * before attempting a potentially privelleged operation. |
306 | | */ |
307 | | PRBool |
308 | | pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx) |
309 | 0 | { |
310 | 0 | return slot->needLogin && !PK11_IsLoggedIn(slot, wincx); |
311 | 0 | } |
312 | | |
313 | | /* |
314 | | * make sure a slot is authenticated... |
315 | | * This function only does the authentication if it is needed. |
316 | | */ |
317 | | SECStatus |
318 | | PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) |
319 | 0 | { |
320 | 0 | if (!slot) { |
321 | 0 | return SECFailure; |
322 | 0 | } |
323 | 0 | if (pk11_LoginStillRequired(slot, wincx)) { |
324 | 0 | return PK11_DoPassword(slot, slot->session, loadCerts, wincx, |
325 | 0 | PR_FALSE, PR_FALSE); |
326 | 0 | } |
327 | 0 | return SECSuccess; |
328 | 0 | } |
329 | | |
330 | | /* |
331 | | * Authenticate to "unfriendly" tokens (tokens which need to be logged |
332 | | * in to find the certs. |
333 | | */ |
334 | | SECStatus |
335 | | pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) |
336 | 0 | { |
337 | 0 | SECStatus rv = SECSuccess; |
338 | 0 | if (!PK11_IsFriendly(slot)) { |
339 | 0 | rv = PK11_Authenticate(slot, loadCerts, wincx); |
340 | 0 | } |
341 | 0 | return rv; |
342 | 0 | } |
343 | | |
344 | | /* |
345 | | * NOTE: this assumes that we are logged out of the card before hand |
346 | | */ |
347 | | SECStatus |
348 | | PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw) |
349 | 0 | { |
350 | 0 | CK_SESSION_HANDLE rwsession; |
351 | 0 | CK_RV crv; |
352 | 0 | SECStatus rv = SECFailure; |
353 | 0 | int len = 0; |
354 | 0 |
|
355 | 0 | /* get a rwsession */ |
356 | 0 | rwsession = PK11_GetRWSession(slot); |
357 | 0 | if (rwsession == CK_INVALID_SESSION) { |
358 | 0 | PORT_SetError(SEC_ERROR_BAD_DATA); |
359 | 0 | return rv; |
360 | 0 | } |
361 | 0 |
|
362 | 0 | if (slot->protectedAuthPath) { |
363 | 0 | len = 0; |
364 | 0 | ssopw = NULL; |
365 | 0 | } else if (ssopw == NULL) { |
366 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
367 | 0 | return SECFailure; |
368 | 0 | } else { |
369 | 0 | len = PORT_Strlen(ssopw); |
370 | 0 | } |
371 | 0 |
|
372 | 0 | /* check the password */ |
373 | 0 | crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO, |
374 | 0 | (unsigned char *)ssopw, len); |
375 | 0 | slot->lastLoginCheck = 0; |
376 | 0 | switch (crv) { |
377 | 0 | /* if we're already logged in, we're good to go */ |
378 | 0 | case CKR_OK: |
379 | 0 | rv = SECSuccess; |
380 | 0 | break; |
381 | 0 | case CKR_PIN_INCORRECT: |
382 | 0 | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
383 | 0 | rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
384 | 0 | break; |
385 | 0 | default: |
386 | 0 | PORT_SetError(PK11_MapError(crv)); |
387 | 0 | rv = SECFailure; /* some failure we can't fix by retrying */ |
388 | 0 | } |
389 | 0 | PK11_GETTAB(slot)->C_Logout(rwsession); |
390 | 0 | slot->lastLoginCheck = 0; |
391 | 0 |
|
392 | 0 | /* release rwsession */ |
393 | 0 | PK11_RestoreROSession(slot, rwsession); |
394 | 0 | return rv; |
395 | 0 | } |
396 | | |
397 | | /* |
398 | | * make sure the password conforms to your token's requirements. |
399 | | */ |
400 | | SECStatus |
401 | | PK11_VerifyPW(PK11SlotInfo *slot, char *pw) |
402 | 0 | { |
403 | 0 | int len = PORT_Strlen(pw); |
404 | 0 |
|
405 | 0 | if ((slot->minPassword > len) || (slot->maxPassword < len)) { |
406 | 0 | PORT_SetError(SEC_ERROR_BAD_DATA); |
407 | 0 | return SECFailure; |
408 | 0 | } |
409 | 0 | return SECSuccess; |
410 | 0 | } |
411 | | |
412 | | /* |
413 | | * initialize a user PIN Value |
414 | | */ |
415 | | SECStatus |
416 | | PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw) |
417 | 0 | { |
418 | 0 | CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION; |
419 | 0 | CK_RV crv; |
420 | 0 | SECStatus rv = SECFailure; |
421 | 0 | int len; |
422 | 0 | int ssolen; |
423 | 0 |
|
424 | 0 | if (userpw == NULL) |
425 | 0 | userpw = ""; |
426 | 0 | if (ssopw == NULL) |
427 | 0 | ssopw = ""; |
428 | 0 |
|
429 | 0 | len = PORT_Strlen(userpw); |
430 | 0 | ssolen = PORT_Strlen(ssopw); |
431 | 0 |
|
432 | 0 | /* get a rwsession */ |
433 | 0 | rwsession = PK11_GetRWSession(slot); |
434 | 0 | if (rwsession == CK_INVALID_SESSION) { |
435 | 0 | PORT_SetError(SEC_ERROR_BAD_DATA); |
436 | 0 | slot->lastLoginCheck = 0; |
437 | 0 | return rv; |
438 | 0 | } |
439 | 0 |
|
440 | 0 | if (slot->protectedAuthPath) { |
441 | 0 | len = 0; |
442 | 0 | ssolen = 0; |
443 | 0 | ssopw = NULL; |
444 | 0 | userpw = NULL; |
445 | 0 | } |
446 | 0 |
|
447 | 0 | /* check the password */ |
448 | 0 | crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO, |
449 | 0 | (unsigned char *)ssopw, ssolen); |
450 | 0 | slot->lastLoginCheck = 0; |
451 | 0 | if (crv != CKR_OK) { |
452 | 0 | PORT_SetError(PK11_MapError(crv)); |
453 | 0 | goto done; |
454 | 0 | } |
455 | 0 |
|
456 | 0 | crv = PK11_GETTAB(slot)->C_InitPIN(rwsession, (unsigned char *)userpw, len); |
457 | 0 | if (crv != CKR_OK) { |
458 | 0 | PORT_SetError(PK11_MapError(crv)); |
459 | 0 | } else { |
460 | 0 | rv = SECSuccess; |
461 | 0 | } |
462 | 0 |
|
463 | 0 | done: |
464 | 0 | PK11_GETTAB(slot)->C_Logout(rwsession); |
465 | 0 | slot->lastLoginCheck = 0; |
466 | 0 | PK11_RestoreROSession(slot, rwsession); |
467 | 0 | if (rv == SECSuccess) { |
468 | 0 | /* update our view of the world */ |
469 | 0 | PK11_InitToken(slot, PR_TRUE); |
470 | 0 | if (slot->needLogin) { |
471 | 0 | PK11_EnterSlotMonitor(slot); |
472 | 0 | PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER, |
473 | 0 | (unsigned char *)userpw, len); |
474 | 0 | slot->lastLoginCheck = 0; |
475 | 0 | PK11_ExitSlotMonitor(slot); |
476 | 0 | } |
477 | 0 | } |
478 | 0 | return rv; |
479 | 0 | } |
480 | | |
481 | | /* |
482 | | * Change an existing user password |
483 | | */ |
484 | | SECStatus |
485 | | PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw, const char *newpw) |
486 | 0 | { |
487 | 0 | CK_RV crv; |
488 | 0 | SECStatus rv = SECFailure; |
489 | 0 | int newLen = 0; |
490 | 0 | int oldLen = 0; |
491 | 0 | CK_SESSION_HANDLE rwsession; |
492 | 0 |
|
493 | 0 | /* use NULL values to trigger the protected authentication path */ |
494 | 0 | if (!slot->protectedAuthPath) { |
495 | 0 | if (newpw == NULL) |
496 | 0 | newpw = ""; |
497 | 0 | if (oldpw == NULL) |
498 | 0 | oldpw = ""; |
499 | 0 | } |
500 | 0 | if (newpw) |
501 | 0 | newLen = PORT_Strlen(newpw); |
502 | 0 | if (oldpw) |
503 | 0 | oldLen = PORT_Strlen(oldpw); |
504 | 0 |
|
505 | 0 | /* get a rwsession */ |
506 | 0 | rwsession = PK11_GetRWSession(slot); |
507 | 0 | if (rwsession == CK_INVALID_SESSION) { |
508 | 0 | PORT_SetError(SEC_ERROR_BAD_DATA); |
509 | 0 | return rv; |
510 | 0 | } |
511 | 0 |
|
512 | 0 | crv = PK11_GETTAB(slot)->C_SetPIN(rwsession, |
513 | 0 | (unsigned char *)oldpw, oldLen, (unsigned char *)newpw, newLen); |
514 | 0 | if (crv == CKR_OK) { |
515 | 0 | rv = SECSuccess; |
516 | 0 | } else { |
517 | 0 | PORT_SetError(PK11_MapError(crv)); |
518 | 0 | } |
519 | 0 |
|
520 | 0 | PK11_RestoreROSession(slot, rwsession); |
521 | 0 |
|
522 | 0 | /* update our view of the world */ |
523 | 0 | PK11_InitToken(slot, PR_TRUE); |
524 | 0 | return rv; |
525 | 0 | } |
526 | | |
527 | | static char * |
528 | | pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void *wincx) |
529 | 0 | { |
530 | 0 | if (PK11_Global.getPass == NULL) |
531 | 0 | return NULL; |
532 | 0 | return (*PK11_Global.getPass)(slot, retry, wincx); |
533 | 0 | } |
534 | | |
535 | | void |
536 | | PK11_SetPasswordFunc(PK11PasswordFunc func) |
537 | 0 | { |
538 | 0 | PK11_Global.getPass = func; |
539 | 0 | } |
540 | | |
541 | | void |
542 | | PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func) |
543 | 0 | { |
544 | 0 | PK11_Global.verifyPass = func; |
545 | 0 | } |
546 | | |
547 | | void |
548 | | PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func) |
549 | 0 | { |
550 | 0 | PK11_Global.isLoggedIn = func; |
551 | 0 | } |
552 | | |
553 | | /* |
554 | | * authenticate to a slot. This loops until we can't recover, the user |
555 | | * gives up, or we succeed. If we're already logged in and this function |
556 | | * is called we will still prompt for a password, but we will probably |
557 | | * succeed no matter what the password was (depending on the implementation |
558 | | * of the PKCS 11 module. |
559 | | */ |
560 | | SECStatus |
561 | | PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, |
562 | | PRBool loadCerts, void *wincx, PRBool alreadyLocked, |
563 | | PRBool contextSpecific) |
564 | | { |
565 | | SECStatus rv = SECFailure; |
566 | | char *password; |
567 | | PRBool attempt = PR_FALSE; |
568 | | |
569 | | if (PK11_NeedUserInit(slot)) { |
570 | | PORT_SetError(SEC_ERROR_IO); |
571 | | return SECFailure; |
572 | | } |
573 | | |
574 | | /* |
575 | | * Central server type applications which control access to multiple |
576 | | * slave applications to single crypto devices need to virtuallize the |
577 | | * login state. This is done by a callback out of PK11_IsLoggedIn and |
578 | | * here. If we are actually logged in, then we got here because the |
579 | | * higher level code told us that the particular client application may |
580 | | * still need to be logged in. If that is the case, we simply tell the |
581 | | * server code that it should now verify the clients password and tell us |
582 | | * the results. |
583 | | */ |
584 | | if (PK11_IsLoggedIn(slot, NULL) && |
585 | | (PK11_Global.verifyPass != NULL)) { |
586 | | if (!PK11_Global.verifyPass(slot, wincx)) { |
587 | | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
588 | | return SECFailure; |
589 | | } |
590 | | return SECSuccess; |
591 | | } |
592 | | |
593 | | /* get the password. This can drop out of the while loop |
594 | | * for the following reasons: |
595 | | * (1) the user refused to enter a password. |
596 | | * (return error to caller) |
597 | | * (2) the token user password is disabled [usually due to |
598 | | * too many failed authentication attempts]. |
599 | | * (return error to caller) |
600 | | * (3) the password was successful. |
601 | | */ |
602 | | while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) { |
603 | | /* if the token has a protectedAuthPath, the application may have |
604 | | * already issued the C_Login as part of it's pk11_GetPassword call. |
605 | | * In this case the application will tell us what the results were in |
606 | | * the password value (retry or the authentication was successful) so |
607 | | * we can skip our own C_Login call (which would force the token to |
608 | | * try to login again). |
609 | | * |
610 | | * Applications that don't know about protectedAuthPath will return a |
611 | | * password, which we will ignore and trigger the token to |
612 | | * 'authenticate' itself anyway. Hopefully the blinking display on |
613 | | * the reader, or the flashing light under the thumbprint reader will |
614 | | * attract the user's attention */ |
615 | | attempt = PR_TRUE; |
616 | | if (slot->protectedAuthPath) { |
617 | | /* application tried to authenticate and failed. it wants to try |
618 | | * again, continue looping */ |
619 | | if (strcmp(password, PK11_PW_RETRY) == 0) { |
620 | | rv = SECWouldBlock; |
621 | | PORT_Free(password); |
622 | | continue; |
623 | | } |
624 | | /* applicaton tried to authenticate and succeeded we're done */ |
625 | | if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) { |
626 | | rv = SECSuccess; |
627 | | PORT_Free(password); |
628 | | break; |
629 | | } |
630 | | } |
631 | | rv = pk11_CheckPassword(slot, session, password, |
632 | | alreadyLocked, contextSpecific); |
633 | | PORT_Memset(password, 0, PORT_Strlen(password)); |
634 | | PORT_Free(password); |
635 | | if (rv != SECWouldBlock) |
636 | | break; |
637 | | } |
638 | | if (rv == SECSuccess) { |
639 | | if (!contextSpecific && !PK11_IsFriendly(slot)) { |
640 | | nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, |
641 | | slot->nssToken); |
642 | | } |
643 | | } else if (!attempt) |
644 | | PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
645 | | return rv; |
646 | | } |
647 | | |
648 | | void |
649 | | PK11_LogoutAll(void) |
650 | 0 | { |
651 | 0 | SECMODListLock *lock = SECMOD_GetDefaultModuleListLock(); |
652 | 0 | SECMODModuleList *modList; |
653 | 0 | SECMODModuleList *mlp = NULL; |
654 | 0 | int i; |
655 | 0 |
|
656 | 0 | /* NSS is not initialized, there are not tokens to log out */ |
657 | 0 | if (lock == NULL) { |
658 | 0 | return; |
659 | 0 | } |
660 | 0 | |
661 | 0 | SECMOD_GetReadLock(lock); |
662 | 0 | modList = SECMOD_GetDefaultModuleList(); |
663 | 0 | /* find the number of entries */ |
664 | 0 | for (mlp = modList; mlp != NULL; mlp = mlp->next) { |
665 | 0 | for (i = 0; i < mlp->module->slotCount; i++) { |
666 | 0 | PK11_Logout(mlp->module->slots[i]); |
667 | 0 | } |
668 | 0 | } |
669 | 0 |
|
670 | 0 | SECMOD_ReleaseReadLock(lock); |
671 | 0 | } |
672 | | |
673 | | int |
674 | | PK11_GetMinimumPwdLength(PK11SlotInfo *slot) |
675 | 0 | { |
676 | 0 | return ((int)slot->minPassword); |
677 | 0 | } |
678 | | |
679 | | /* Does this slot have a protected pin path? */ |
680 | | PRBool |
681 | | PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot) |
682 | 0 | { |
683 | 0 | return slot->protectedAuthPath; |
684 | 0 | } |
685 | | |
686 | | /* |
687 | | * we can initialize the password if 1) The toke is not inited |
688 | | * (need login == true and see need UserInit) or 2) the token has |
689 | | * a NULL password. (slot->needLogin = false & need user Init = false). |
690 | | */ |
691 | | PRBool |
692 | | PK11_NeedPWInitForSlot(PK11SlotInfo *slot) |
693 | 0 | { |
694 | 0 | if (slot->needLogin && PK11_NeedUserInit(slot)) { |
695 | 0 | return PR_TRUE; |
696 | 0 | } |
697 | 0 | if (!slot->needLogin && !PK11_NeedUserInit(slot)) { |
698 | 0 | return PR_TRUE; |
699 | 0 | } |
700 | 0 | return PR_FALSE; |
701 | 0 | } |
702 | | |
703 | | PRBool |
704 | | PK11_NeedPWInit() |
705 | 0 | { |
706 | 0 | PK11SlotInfo *slot = PK11_GetInternalKeySlot(); |
707 | 0 | PRBool ret = PR_FALSE; |
708 | 0 | if (slot) { |
709 | 0 | ret = PK11_NeedPWInitForSlot(slot); |
710 | 0 | PK11_FreeSlot(slot); |
711 | 0 | } |
712 | 0 | return ret; |
713 | 0 | } |
714 | | |
715 | | PRBool |
716 | | pk11_InDelayPeriod(PRIntervalTime lastTime, PRIntervalTime delayTime, |
717 | | PRIntervalTime *retTime) |
718 | 0 | { |
719 | 0 | PRIntervalTime time; |
720 | 0 |
|
721 | 0 | *retTime = time = PR_IntervalNow(); |
722 | 0 | return (PRBool)(lastTime) && ((time - lastTime) < delayTime); |
723 | 0 | } |
724 | | |
725 | | /* |
726 | | * Determine if the token is logged in. We have to actually query the token, |
727 | | * because it's state can change without intervention from us. |
728 | | */ |
729 | | PRBool |
730 | | PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx) |
731 | 0 | { |
732 | 0 | CK_SESSION_INFO sessionInfo; |
733 | 0 | int askpw = slot->askpw; |
734 | 0 | int timeout = slot->timeout; |
735 | 0 | CK_RV crv; |
736 | 0 | PRIntervalTime curTime; |
737 | 0 | static PRIntervalTime login_delay_time = 0; |
738 | 0 |
|
739 | 0 | if (login_delay_time == 0) { |
740 | 0 | login_delay_time = PR_SecondsToInterval(1); |
741 | 0 | } |
742 | 0 |
|
743 | 0 | /* If we don't have our own password default values, use the system |
744 | 0 | * ones */ |
745 | 0 | if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
746 | 0 | PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
747 | 0 |
|
748 | 0 | if (def_slot) { |
749 | 0 | askpw = def_slot->askpw; |
750 | 0 | timeout = def_slot->timeout; |
751 | 0 | PK11_FreeSlot(def_slot); |
752 | 0 | } |
753 | 0 | } |
754 | 0 |
|
755 | 0 | if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) && |
756 | 0 | (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { |
757 | 0 | return PR_FALSE; |
758 | 0 | } |
759 | 0 |
|
760 | 0 | /* forget the password if we've been inactive too long */ |
761 | 0 | if (askpw == 1) { |
762 | 0 | PRTime currtime = PR_Now(); |
763 | 0 | PRTime result; |
764 | 0 | PRTime mult; |
765 | 0 |
|
766 | 0 | LL_I2L(result, timeout); |
767 | 0 | LL_I2L(mult, 60 * 1000 * 1000); |
768 | 0 | LL_MUL(result, result, mult); |
769 | 0 | LL_ADD(result, result, slot->authTime); |
770 | 0 | if (LL_CMP(result, <, currtime)) { |
771 | 0 | PK11_EnterSlotMonitor(slot); |
772 | 0 | PK11_GETTAB(slot)->C_Logout(slot->session); |
773 | 0 | slot->lastLoginCheck = 0; |
774 | 0 | PK11_ExitSlotMonitor(slot); |
775 | 0 | } else { |
776 | 0 | slot->authTime = currtime; |
777 | 0 | } |
778 | 0 | } |
779 | 0 |
|
780 | 0 | PK11_EnterSlotMonitor(slot); |
781 | 0 | if (pk11_InDelayPeriod(slot->lastLoginCheck, login_delay_time, &curTime)) { |
782 | 0 | sessionInfo.state = slot->lastState; |
783 | 0 | crv = CKR_OK; |
784 | 0 | } else { |
785 | 0 | crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo); |
786 | 0 | if (crv == CKR_OK) { |
787 | 0 | slot->lastState = sessionInfo.state; |
788 | 0 | slot->lastLoginCheck = curTime; |
789 | 0 | } |
790 | 0 | } |
791 | 0 | PK11_ExitSlotMonitor(slot); |
792 | 0 | /* if we can't get session info, something is really wrong */ |
793 | 0 | if (crv != CKR_OK) { |
794 | 0 | slot->session = CK_INVALID_SESSION; |
795 | 0 | return PR_FALSE; |
796 | 0 | } |
797 | 0 |
|
798 | 0 | switch (sessionInfo.state) { |
799 | 0 | case CKS_RW_PUBLIC_SESSION: |
800 | 0 | case CKS_RO_PUBLIC_SESSION: |
801 | 0 | default: |
802 | 0 | break; /* fail */ |
803 | 0 | case CKS_RW_USER_FUNCTIONS: |
804 | 0 | case CKS_RW_SO_FUNCTIONS: |
805 | 0 | case CKS_RO_USER_FUNCTIONS: |
806 | 0 | return PR_TRUE; |
807 | 0 | } |
808 | 0 | return PR_FALSE; |
809 | 0 | } |