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