1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pk11wrap/pk11auth.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,788 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +/* 1.8 + * This file deals with PKCS #11 passwords and authentication. 1.9 + */ 1.10 +#include "seccomon.h" 1.11 +#include "secmod.h" 1.12 +#include "secmodi.h" 1.13 +#include "secmodti.h" 1.14 +#include "pkcs11t.h" 1.15 +#include "pk11func.h" 1.16 +#include "secitem.h" 1.17 +#include "secerr.h" 1.18 + 1.19 +#include "pkim.h" 1.20 + 1.21 + 1.22 +/************************************************************* 1.23 + * local static and global data 1.24 + *************************************************************/ 1.25 +/* 1.26 + * This structure keeps track of status that spans all the Slots. 1.27 + * NOTE: This is a global data structure. It semantics expect thread crosstalk 1.28 + * be very careful when you see it used. 1.29 + * It's major purpose in life is to allow the user to log in one PER 1.30 + * Tranaction, even if a transaction spans threads. The problem is the user 1.31 + * may have to enter a password one just to be able to look at the 1.32 + * personalities/certificates (s)he can use. Then if Auth every is one, they 1.33 + * may have to enter the password again to use the card. See PK11_StartTransac 1.34 + * and PK11_EndTransaction. 1.35 + */ 1.36 +static struct PK11GlobalStruct { 1.37 + int transaction; 1.38 + PRBool inTransaction; 1.39 + char *(PR_CALLBACK *getPass)(PK11SlotInfo *,PRBool,void *); 1.40 + PRBool (PR_CALLBACK *verifyPass)(PK11SlotInfo *,void *); 1.41 + PRBool (PR_CALLBACK *isLoggedIn)(PK11SlotInfo *,void *); 1.42 +} PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL }; 1.43 + 1.44 +/*********************************************************** 1.45 + * Password Utilities 1.46 + ***********************************************************/ 1.47 +/* 1.48 + * Check the user's password. Log into the card if it's correct. 1.49 + * succeed if the user is already logged in. 1.50 + */ 1.51 +static SECStatus 1.52 +pk11_CheckPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, 1.53 + char *pw, PRBool alreadyLocked, PRBool contextSpecific) 1.54 +{ 1.55 + int len = 0; 1.56 + CK_RV crv; 1.57 + SECStatus rv; 1.58 + PRTime currtime = PR_Now(); 1.59 + PRBool mustRetry; 1.60 + int retry = 0; 1.61 + 1.62 + if (slot->protectedAuthPath) { 1.63 + len = 0; 1.64 + pw = NULL; 1.65 + } else if (pw == NULL) { 1.66 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.67 + return SECFailure; 1.68 + } else { 1.69 + len = PORT_Strlen(pw); 1.70 + } 1.71 + 1.72 + do { 1.73 + if (!alreadyLocked) PK11_EnterSlotMonitor(slot); 1.74 + crv = PK11_GETTAB(slot)->C_Login(session, 1.75 + contextSpecific ? CKU_CONTEXT_SPECIFIC : CKU_USER, 1.76 + (unsigned char *)pw,len); 1.77 + slot->lastLoginCheck = 0; 1.78 + mustRetry = PR_FALSE; 1.79 + if (!alreadyLocked) PK11_ExitSlotMonitor(slot); 1.80 + switch (crv) { 1.81 + /* if we're already logged in, we're good to go */ 1.82 + case CKR_OK: 1.83 + /* TODO If it was for CKU_CONTEXT_SPECIFIC should we do this */ 1.84 + slot->authTransact = PK11_Global.transaction; 1.85 + /* Fall through */ 1.86 + case CKR_USER_ALREADY_LOGGED_IN: 1.87 + slot->authTime = currtime; 1.88 + rv = SECSuccess; 1.89 + break; 1.90 + case CKR_PIN_INCORRECT: 1.91 + PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.92 + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ 1.93 + break; 1.94 + /* someone called reset while we fetched the password, try again once 1.95 + * if the token is still there. */ 1.96 + case CKR_SESSION_HANDLE_INVALID: 1.97 + case CKR_SESSION_CLOSED: 1.98 + if (session != slot->session) { 1.99 + /* don't bother retrying, we were in a middle of an operation, 1.100 + * which is now lost. Just fail. */ 1.101 + PORT_SetError(PK11_MapError(crv)); 1.102 + rv = SECFailure; 1.103 + break; 1.104 + } 1.105 + if (retry++ == 0) { 1.106 + rv = PK11_InitToken(slot,PR_FALSE); 1.107 + if (rv == SECSuccess) { 1.108 + if (slot->session != CK_INVALID_SESSION) { 1.109 + session = slot->session; /* we should have 1.110 + * a new session now */ 1.111 + mustRetry = PR_TRUE; 1.112 + } else { 1.113 + PORT_SetError(PK11_MapError(crv)); 1.114 + rv = SECFailure; 1.115 + } 1.116 + } 1.117 + break; 1.118 + } 1.119 + /* Fall through */ 1.120 + default: 1.121 + PORT_SetError(PK11_MapError(crv)); 1.122 + rv = SECFailure; /* some failure we can't fix by retrying */ 1.123 + } 1.124 + } while (mustRetry); 1.125 + return rv; 1.126 +} 1.127 + 1.128 +/* 1.129 + * Check the user's password. Logout before hand to make sure that 1.130 + * we are really checking the password. 1.131 + */ 1.132 +SECStatus 1.133 +PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw) 1.134 +{ 1.135 + int len = 0; 1.136 + CK_RV crv; 1.137 + SECStatus rv; 1.138 + PRTime currtime = PR_Now(); 1.139 + 1.140 + if (slot->protectedAuthPath) { 1.141 + len = 0; 1.142 + pw = NULL; 1.143 + } else if (pw == NULL) { 1.144 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.145 + return SECFailure; 1.146 + } else { 1.147 + len = PORT_Strlen(pw); 1.148 + } 1.149 + 1.150 + /* 1.151 + * If the token doesn't need a login, don't try to relogin because the 1.152 + * effect is undefined. It's not clear what it means to check a non-empty 1.153 + * password with such a token, so treat that as an error. 1.154 + */ 1.155 + if (!slot->needLogin) { 1.156 + if (len == 0) { 1.157 + rv = SECSuccess; 1.158 + } else { 1.159 + PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.160 + rv = SECFailure; 1.161 + } 1.162 + return rv; 1.163 + } 1.164 + 1.165 + /* force a logout */ 1.166 + PK11_EnterSlotMonitor(slot); 1.167 + PK11_GETTAB(slot)->C_Logout(slot->session); 1.168 + 1.169 + crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, 1.170 + (unsigned char *)pw,len); 1.171 + slot->lastLoginCheck = 0; 1.172 + PK11_ExitSlotMonitor(slot); 1.173 + switch (crv) { 1.174 + /* if we're already logged in, we're good to go */ 1.175 + case CKR_OK: 1.176 + slot->authTransact = PK11_Global.transaction; 1.177 + slot->authTime = currtime; 1.178 + rv = SECSuccess; 1.179 + break; 1.180 + case CKR_PIN_INCORRECT: 1.181 + PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.182 + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ 1.183 + break; 1.184 + default: 1.185 + PORT_SetError(PK11_MapError(crv)); 1.186 + rv = SECFailure; /* some failure we can't fix by retrying */ 1.187 + } 1.188 + return rv; 1.189 +} 1.190 + 1.191 +SECStatus 1.192 +PK11_Logout(PK11SlotInfo *slot) 1.193 +{ 1.194 + CK_RV crv; 1.195 + 1.196 + /* force a logout */ 1.197 + PK11_EnterSlotMonitor(slot); 1.198 + crv = PK11_GETTAB(slot)->C_Logout(slot->session); 1.199 + slot->lastLoginCheck = 0; 1.200 + PK11_ExitSlotMonitor(slot); 1.201 + if (crv != CKR_OK) { 1.202 + PORT_SetError(PK11_MapError(crv)); 1.203 + return SECFailure; 1.204 + } 1.205 + return SECSuccess; 1.206 +} 1.207 + 1.208 +/* 1.209 + * transaction stuff is for when we test for the need to do every 1.210 + * time auth to see if we already did it for this slot/transaction 1.211 + */ 1.212 +void PK11_StartAuthTransaction(void) 1.213 +{ 1.214 +PK11_Global.transaction++; 1.215 +PK11_Global.inTransaction = PR_TRUE; 1.216 +} 1.217 + 1.218 +void PK11_EndAuthTransaction(void) 1.219 +{ 1.220 +PK11_Global.transaction++; 1.221 +PK11_Global.inTransaction = PR_FALSE; 1.222 +} 1.223 + 1.224 +/* 1.225 + * before we do a private key op, we check to see if we 1.226 + * need to reauthenticate. 1.227 + */ 1.228 +void 1.229 +PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx) 1.230 +{ 1.231 + int askpw = slot->askpw; 1.232 + PRBool NeedAuth = PR_FALSE; 1.233 + 1.234 + if (!slot->needLogin) return; 1.235 + 1.236 + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { 1.237 + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); 1.238 + 1.239 + if (def_slot) { 1.240 + askpw = def_slot->askpw; 1.241 + PK11_FreeSlot(def_slot); 1.242 + } 1.243 + } 1.244 + 1.245 + /* timeouts are handled by isLoggedIn */ 1.246 + if (!PK11_IsLoggedIn(slot,wincx)) { 1.247 + NeedAuth = PR_TRUE; 1.248 + } else if (askpw == -1) { 1.249 + if (!PK11_Global.inTransaction || 1.250 + (PK11_Global.transaction != slot->authTransact)) { 1.251 + PK11_EnterSlotMonitor(slot); 1.252 + PK11_GETTAB(slot)->C_Logout(slot->session); 1.253 + slot->lastLoginCheck = 0; 1.254 + PK11_ExitSlotMonitor(slot); 1.255 + NeedAuth = PR_TRUE; 1.256 + } 1.257 + } 1.258 + if (NeedAuth) PK11_DoPassword(slot, slot->session, PR_TRUE, 1.259 + wincx, PR_FALSE, PR_FALSE); 1.260 +} 1.261 + 1.262 +void 1.263 +PK11_SlotDBUpdate(PK11SlotInfo *slot) 1.264 +{ 1.265 + SECMOD_UpdateModule(slot->module); 1.266 +} 1.267 + 1.268 +/* 1.269 + * set new askpw and timeout values 1.270 + */ 1.271 +void 1.272 +PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout) 1.273 +{ 1.274 + slot->askpw = askpw; 1.275 + slot->timeout = timeout; 1.276 + slot->defaultFlags |= PK11_OWN_PW_DEFAULTS; 1.277 + PK11_SlotDBUpdate(slot); 1.278 +} 1.279 + 1.280 +/* 1.281 + * Get the askpw and timeout values for this slot 1.282 + */ 1.283 +void 1.284 +PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout) 1.285 +{ 1.286 + *askpw = slot->askpw; 1.287 + *timeout = slot->timeout; 1.288 + 1.289 + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { 1.290 + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); 1.291 + 1.292 + if (def_slot) { 1.293 + *askpw = def_slot->askpw; 1.294 + *timeout = def_slot->timeout; 1.295 + PK11_FreeSlot(def_slot); 1.296 + } 1.297 + } 1.298 +} 1.299 + 1.300 +/* 1.301 + * Returns true if the token is needLogin and isn't logged in. 1.302 + * This function is used to determine if authentication is needed 1.303 + * before attempting a potentially privelleged operation. 1.304 + */ 1.305 +PRBool 1.306 +pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx) 1.307 +{ 1.308 + return slot->needLogin && !PK11_IsLoggedIn(slot,wincx); 1.309 +} 1.310 + 1.311 +/* 1.312 + * make sure a slot is authenticated... 1.313 + * This function only does the authentication if it is needed. 1.314 + */ 1.315 +SECStatus 1.316 +PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) { 1.317 + if (pk11_LoginStillRequired(slot,wincx)) { 1.318 + return PK11_DoPassword(slot, slot->session, loadCerts, wincx, 1.319 + PR_FALSE, PR_FALSE); 1.320 + } 1.321 + return SECSuccess; 1.322 +} 1.323 + 1.324 +/* 1.325 + * Authenticate to "unfriendly" tokens (tokens which need to be logged 1.326 + * in to find the certs. 1.327 + */ 1.328 +SECStatus 1.329 +pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) 1.330 +{ 1.331 + SECStatus rv = SECSuccess; 1.332 + if (!PK11_IsFriendly(slot)) { 1.333 + rv = PK11_Authenticate(slot, loadCerts, wincx); 1.334 + } 1.335 + return rv; 1.336 +} 1.337 + 1.338 + 1.339 +/* 1.340 + * NOTE: this assumes that we are logged out of the card before hand 1.341 + */ 1.342 +SECStatus 1.343 +PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw) 1.344 +{ 1.345 + CK_SESSION_HANDLE rwsession; 1.346 + CK_RV crv; 1.347 + SECStatus rv = SECFailure; 1.348 + int len = 0; 1.349 + 1.350 + /* get a rwsession */ 1.351 + rwsession = PK11_GetRWSession(slot); 1.352 + if (rwsession == CK_INVALID_SESSION) { 1.353 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.354 + return rv; 1.355 + } 1.356 + 1.357 + if (slot->protectedAuthPath) { 1.358 + len = 0; 1.359 + ssopw = NULL; 1.360 + } else if (ssopw == NULL) { 1.361 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.362 + return SECFailure; 1.363 + } else { 1.364 + len = PORT_Strlen(ssopw); 1.365 + } 1.366 + 1.367 + /* check the password */ 1.368 + crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO, 1.369 + (unsigned char *)ssopw,len); 1.370 + slot->lastLoginCheck = 0; 1.371 + switch (crv) { 1.372 + /* if we're already logged in, we're good to go */ 1.373 + case CKR_OK: 1.374 + rv = SECSuccess; 1.375 + break; 1.376 + case CKR_PIN_INCORRECT: 1.377 + PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.378 + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ 1.379 + break; 1.380 + default: 1.381 + PORT_SetError(PK11_MapError(crv)); 1.382 + rv = SECFailure; /* some failure we can't fix by retrying */ 1.383 + } 1.384 + PK11_GETTAB(slot)->C_Logout(rwsession); 1.385 + slot->lastLoginCheck = 0; 1.386 + 1.387 + /* release rwsession */ 1.388 + PK11_RestoreROSession(slot,rwsession); 1.389 + return rv; 1.390 +} 1.391 + 1.392 +/* 1.393 + * make sure the password conforms to your token's requirements. 1.394 + */ 1.395 +SECStatus 1.396 +PK11_VerifyPW(PK11SlotInfo *slot,char *pw) 1.397 +{ 1.398 + int len = PORT_Strlen(pw); 1.399 + 1.400 + if ((slot->minPassword > len) || (slot->maxPassword < len)) { 1.401 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.402 + return SECFailure; 1.403 + } 1.404 + return SECSuccess; 1.405 +} 1.406 + 1.407 +/* 1.408 + * initialize a user PIN Value 1.409 + */ 1.410 +SECStatus 1.411 +PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw) 1.412 +{ 1.413 + CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION; 1.414 + CK_RV crv; 1.415 + SECStatus rv = SECFailure; 1.416 + int len; 1.417 + int ssolen; 1.418 + 1.419 + if (userpw == NULL) userpw = ""; 1.420 + if (ssopw == NULL) ssopw = ""; 1.421 + 1.422 + len = PORT_Strlen(userpw); 1.423 + ssolen = PORT_Strlen(ssopw); 1.424 + 1.425 + /* get a rwsession */ 1.426 + rwsession = PK11_GetRWSession(slot); 1.427 + if (rwsession == CK_INVALID_SESSION) { 1.428 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.429 + slot->lastLoginCheck = 0; 1.430 + return rv; 1.431 + } 1.432 + 1.433 + if (slot->protectedAuthPath) { 1.434 + len = 0; 1.435 + ssolen = 0; 1.436 + ssopw = NULL; 1.437 + userpw = NULL; 1.438 + } 1.439 + 1.440 + /* check the password */ 1.441 + crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO, 1.442 + (unsigned char *)ssopw,ssolen); 1.443 + slot->lastLoginCheck = 0; 1.444 + if (crv != CKR_OK) { 1.445 + PORT_SetError(PK11_MapError(crv)); 1.446 + goto done; 1.447 + } 1.448 + 1.449 + crv = PK11_GETTAB(slot)->C_InitPIN(rwsession,(unsigned char *)userpw,len); 1.450 + if (crv != CKR_OK) { 1.451 + PORT_SetError(PK11_MapError(crv)); 1.452 + } else { 1.453 + rv = SECSuccess; 1.454 + } 1.455 + 1.456 +done: 1.457 + PK11_GETTAB(slot)->C_Logout(rwsession); 1.458 + slot->lastLoginCheck = 0; 1.459 + PK11_RestoreROSession(slot,rwsession); 1.460 + if (rv == SECSuccess) { 1.461 + /* update our view of the world */ 1.462 + PK11_InitToken(slot,PR_TRUE); 1.463 + if (slot->needLogin) { 1.464 + PK11_EnterSlotMonitor(slot); 1.465 + PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, 1.466 + (unsigned char *)userpw,len); 1.467 + slot->lastLoginCheck = 0; 1.468 + PK11_ExitSlotMonitor(slot); 1.469 + } 1.470 + } 1.471 + return rv; 1.472 +} 1.473 + 1.474 +/* 1.475 + * Change an existing user password 1.476 + */ 1.477 +SECStatus 1.478 +PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw, const char *newpw) 1.479 +{ 1.480 + CK_RV crv; 1.481 + SECStatus rv = SECFailure; 1.482 + int newLen = 0; 1.483 + int oldLen = 0; 1.484 + CK_SESSION_HANDLE rwsession; 1.485 + 1.486 + /* use NULL values to trigger the protected authentication path */ 1.487 + if (!slot->protectedAuthPath) { 1.488 + if (newpw == NULL) newpw = ""; 1.489 + if (oldpw == NULL) oldpw = ""; 1.490 + } 1.491 + if (newpw) newLen = PORT_Strlen(newpw); 1.492 + if (oldpw) oldLen = PORT_Strlen(oldpw); 1.493 + 1.494 + /* get a rwsession */ 1.495 + rwsession = PK11_GetRWSession(slot); 1.496 + if (rwsession == CK_INVALID_SESSION) { 1.497 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.498 + return rv; 1.499 + } 1.500 + 1.501 + crv = PK11_GETTAB(slot)->C_SetPIN(rwsession, 1.502 + (unsigned char *)oldpw,oldLen,(unsigned char *)newpw,newLen); 1.503 + if (crv == CKR_OK) { 1.504 + rv = SECSuccess; 1.505 + } else { 1.506 + PORT_SetError(PK11_MapError(crv)); 1.507 + } 1.508 + 1.509 + PK11_RestoreROSession(slot,rwsession); 1.510 + 1.511 + /* update our view of the world */ 1.512 + PK11_InitToken(slot,PR_TRUE); 1.513 + return rv; 1.514 +} 1.515 + 1.516 +static char * 1.517 +pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void * wincx) 1.518 +{ 1.519 + if (PK11_Global.getPass == NULL) return NULL; 1.520 + return (*PK11_Global.getPass)(slot, retry, wincx); 1.521 +} 1.522 + 1.523 +void 1.524 +PK11_SetPasswordFunc(PK11PasswordFunc func) 1.525 +{ 1.526 + PK11_Global.getPass = func; 1.527 +} 1.528 + 1.529 +void 1.530 +PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func) 1.531 +{ 1.532 + PK11_Global.verifyPass = func; 1.533 +} 1.534 + 1.535 +void 1.536 +PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func) 1.537 +{ 1.538 + PK11_Global.isLoggedIn = func; 1.539 +} 1.540 + 1.541 + 1.542 +/* 1.543 + * authenticate to a slot. This loops until we can't recover, the user 1.544 + * gives up, or we succeed. If we're already logged in and this function 1.545 + * is called we will still prompt for a password, but we will probably 1.546 + * succeed no matter what the password was (depending on the implementation 1.547 + * of the PKCS 11 module. 1.548 + */ 1.549 +SECStatus 1.550 +PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, 1.551 + PRBool loadCerts, void *wincx, PRBool alreadyLocked, 1.552 + PRBool contextSpecific) 1.553 +{ 1.554 + SECStatus rv = SECFailure; 1.555 + char * password; 1.556 + PRBool attempt = PR_FALSE; 1.557 + 1.558 + if (PK11_NeedUserInit(slot)) { 1.559 + PORT_SetError(SEC_ERROR_IO); 1.560 + return SECFailure; 1.561 + } 1.562 + 1.563 + 1.564 + /* 1.565 + * Central server type applications which control access to multiple 1.566 + * slave applications to single crypto devices need to virtuallize the 1.567 + * login state. This is done by a callback out of PK11_IsLoggedIn and 1.568 + * here. If we are actually logged in, then we got here because the 1.569 + * higher level code told us that the particular client application may 1.570 + * still need to be logged in. If that is the case, we simply tell the 1.571 + * server code that it should now verify the clients password and tell us 1.572 + * the results. 1.573 + */ 1.574 + if (PK11_IsLoggedIn(slot,NULL) && 1.575 + (PK11_Global.verifyPass != NULL)) { 1.576 + if (!PK11_Global.verifyPass(slot,wincx)) { 1.577 + PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.578 + return SECFailure; 1.579 + } 1.580 + return SECSuccess; 1.581 + } 1.582 + 1.583 + /* get the password. This can drop out of the while loop 1.584 + * for the following reasons: 1.585 + * (1) the user refused to enter a password. 1.586 + * (return error to caller) 1.587 + * (2) the token user password is disabled [usually due to 1.588 + * too many failed authentication attempts]. 1.589 + * (return error to caller) 1.590 + * (3) the password was successful. 1.591 + */ 1.592 + while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) { 1.593 + /* if the token has a protectedAuthPath, the application may have 1.594 + * already issued the C_Login as part of it's pk11_GetPassword call. 1.595 + * In this case the application will tell us what the results were in 1.596 + * the password value (retry or the authentication was successful) so 1.597 + * we can skip our own C_Login call (which would force the token to 1.598 + * try to login again). 1.599 + * 1.600 + * Applications that don't know about protectedAuthPath will return a 1.601 + * password, which we will ignore and trigger the token to 1.602 + * 'authenticate' itself anyway. Hopefully the blinking display on 1.603 + * the reader, or the flashing light under the thumbprint reader will 1.604 + * attract the user's attention */ 1.605 + attempt = PR_TRUE; 1.606 + if (slot->protectedAuthPath) { 1.607 + /* application tried to authenticate and failed. it wants to try 1.608 + * again, continue looping */ 1.609 + if (strcmp(password, PK11_PW_RETRY) == 0) { 1.610 + rv = SECWouldBlock; 1.611 + PORT_Free(password); 1.612 + continue; 1.613 + } 1.614 + /* applicaton tried to authenticate and succeeded we're done */ 1.615 + if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) { 1.616 + rv = SECSuccess; 1.617 + PORT_Free(password); 1.618 + break; 1.619 + } 1.620 + } 1.621 + rv = pk11_CheckPassword(slot, session, password, 1.622 + alreadyLocked, contextSpecific); 1.623 + PORT_Memset(password, 0, PORT_Strlen(password)); 1.624 + PORT_Free(password); 1.625 + if (rv != SECWouldBlock) break; 1.626 + } 1.627 + if (rv == SECSuccess) { 1.628 + if (!PK11_IsFriendly(slot)) { 1.629 + nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, 1.630 + slot->nssToken); 1.631 + } 1.632 + } else if (!attempt) PORT_SetError(SEC_ERROR_BAD_PASSWORD); 1.633 + return rv; 1.634 +} 1.635 + 1.636 +void PK11_LogoutAll(void) 1.637 +{ 1.638 + SECMODListLock *lock = SECMOD_GetDefaultModuleListLock(); 1.639 + SECMODModuleList *modList; 1.640 + SECMODModuleList *mlp = NULL; 1.641 + int i; 1.642 + 1.643 + /* NSS is not initialized, there are not tokens to log out */ 1.644 + if (lock == NULL) { 1.645 + return; 1.646 + } 1.647 + 1.648 + SECMOD_GetReadLock(lock); 1.649 + modList = SECMOD_GetDefaultModuleList(); 1.650 + /* find the number of entries */ 1.651 + for (mlp = modList; mlp != NULL; mlp = mlp->next) { 1.652 + for (i=0; i < mlp->module->slotCount; i++) { 1.653 + PK11_Logout(mlp->module->slots[i]); 1.654 + } 1.655 + } 1.656 + 1.657 + SECMOD_ReleaseReadLock(lock); 1.658 +} 1.659 + 1.660 +int 1.661 +PK11_GetMinimumPwdLength(PK11SlotInfo *slot) 1.662 +{ 1.663 + return ((int)slot->minPassword); 1.664 +} 1.665 + 1.666 +/* Does this slot have a protected pin path? */ 1.667 +PRBool 1.668 +PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot) 1.669 +{ 1.670 + return slot->protectedAuthPath; 1.671 +} 1.672 + 1.673 +/* 1.674 + * we can initialize the password if 1) The toke is not inited 1.675 + * (need login == true and see need UserInit) or 2) the token has 1.676 + * a NULL password. (slot->needLogin = false & need user Init = false). 1.677 + */ 1.678 +PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot) 1.679 +{ 1.680 + if (slot->needLogin && PK11_NeedUserInit(slot)) { 1.681 + return PR_TRUE; 1.682 + } 1.683 + if (!slot->needLogin && !PK11_NeedUserInit(slot)) { 1.684 + return PR_TRUE; 1.685 + } 1.686 + return PR_FALSE; 1.687 +} 1.688 + 1.689 +PRBool PK11_NeedPWInit() 1.690 +{ 1.691 + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); 1.692 + PRBool ret = PK11_NeedPWInitForSlot(slot); 1.693 + 1.694 + PK11_FreeSlot(slot); 1.695 + return ret; 1.696 +} 1.697 + 1.698 +PRBool 1.699 +pk11_InDelayPeriod(PRIntervalTime lastTime, PRIntervalTime delayTime, 1.700 + PRIntervalTime *retTime) 1.701 +{ 1.702 + PRIntervalTime time; 1.703 + 1.704 + *retTime = time = PR_IntervalNow(); 1.705 + return (PRBool) (lastTime) && ((time-lastTime) < delayTime); 1.706 +} 1.707 + 1.708 +/* 1.709 + * Determine if the token is logged in. We have to actually query the token, 1.710 + * because it's state can change without intervention from us. 1.711 + */ 1.712 +PRBool 1.713 +PK11_IsLoggedIn(PK11SlotInfo *slot,void *wincx) 1.714 +{ 1.715 + CK_SESSION_INFO sessionInfo; 1.716 + int askpw = slot->askpw; 1.717 + int timeout = slot->timeout; 1.718 + CK_RV crv; 1.719 + PRIntervalTime curTime; 1.720 + static PRIntervalTime login_delay_time = 0; 1.721 + 1.722 + if (login_delay_time == 0) { 1.723 + login_delay_time = PR_SecondsToInterval(1); 1.724 + } 1.725 + 1.726 + /* If we don't have our own password default values, use the system 1.727 + * ones */ 1.728 + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { 1.729 + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); 1.730 + 1.731 + if (def_slot) { 1.732 + askpw = def_slot->askpw; 1.733 + timeout = def_slot->timeout; 1.734 + PK11_FreeSlot(def_slot); 1.735 + } 1.736 + } 1.737 + 1.738 + if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) && 1.739 + (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { return PR_FALSE; } 1.740 + 1.741 + 1.742 + /* forget the password if we've been inactive too long */ 1.743 + if (askpw == 1) { 1.744 + PRTime currtime = PR_Now(); 1.745 + PRTime result; 1.746 + PRTime mult; 1.747 + 1.748 + LL_I2L(result, timeout); 1.749 + LL_I2L(mult, 60*1000*1000); 1.750 + LL_MUL(result,result,mult); 1.751 + LL_ADD(result, result, slot->authTime); 1.752 + if (LL_CMP(result, <, currtime) ) { 1.753 + PK11_EnterSlotMonitor(slot); 1.754 + PK11_GETTAB(slot)->C_Logout(slot->session); 1.755 + slot->lastLoginCheck = 0; 1.756 + PK11_ExitSlotMonitor(slot); 1.757 + } else { 1.758 + slot->authTime = currtime; 1.759 + } 1.760 + } 1.761 + 1.762 + PK11_EnterSlotMonitor(slot); 1.763 + if (pk11_InDelayPeriod(slot->lastLoginCheck,login_delay_time, &curTime)) { 1.764 + sessionInfo.state = slot->lastState; 1.765 + crv = CKR_OK; 1.766 + } else { 1.767 + crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo); 1.768 + if (crv == CKR_OK) { 1.769 + slot->lastState = sessionInfo.state; 1.770 + slot->lastLoginCheck = curTime; 1.771 + } 1.772 + } 1.773 + PK11_ExitSlotMonitor(slot); 1.774 + /* if we can't get session info, something is really wrong */ 1.775 + if (crv != CKR_OK) { 1.776 + slot->session = CK_INVALID_SESSION; 1.777 + return PR_FALSE; 1.778 + } 1.779 + 1.780 + switch (sessionInfo.state) { 1.781 + case CKS_RW_PUBLIC_SESSION: 1.782 + case CKS_RO_PUBLIC_SESSION: 1.783 + default: 1.784 + break; /* fail */ 1.785 + case CKS_RW_USER_FUNCTIONS: 1.786 + case CKS_RW_SO_FUNCTIONS: 1.787 + case CKS_RO_USER_FUNCTIONS: 1.788 + return PR_TRUE; 1.789 + } 1.790 + return PR_FALSE; 1.791 +}