| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * This file deals with PKCS #11 passwords and authentication. |
| */ |
| #include "seccomon.h" |
| #include "secmod.h" |
| #include "secmodi.h" |
| #include "secmodti.h" |
| #include "pkcs11t.h" |
| #include "pk11func.h" |
| #include "secitem.h" |
| #include "secerr.h" |
| |
| #include "pkim.h" |
| |
| /************************************************************* |
| * local static and global data |
| *************************************************************/ |
| /* |
| * This structure keeps track of status that spans all the Slots. |
| * NOTE: This is a global data structure. It semantics expect thread crosstalk |
| * be very careful when you see it used. |
| * It's major purpose in life is to allow the user to log in one PER |
| * Tranaction, even if a transaction spans threads. The problem is the user |
| * may have to enter a password one just to be able to look at the |
| * personalities/certificates (s)he can use. Then if Auth every is one, they |
| * may have to enter the password again to use the card. See PK11_StartTransac |
| * and PK11_EndTransaction. |
| */ |
| static struct PK11GlobalStruct { |
| int transaction; |
| PRBool inTransaction; |
| char *(PR_CALLBACK *getPass)(PK11SlotInfo *, PRBool, void *); |
| PRBool(PR_CALLBACK *verifyPass)(PK11SlotInfo *, void *); |
| PRBool(PR_CALLBACK *isLoggedIn)(PK11SlotInfo *, void *); |
| } PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL }; |
| |
| /*********************************************************** |
| * Password Utilities |
| ***********************************************************/ |
| /* |
| * Check the user's password. Log into the card if it's correct. |
| * succeed if the user is already logged in. |
| */ |
| static SECStatus |
| pk11_CheckPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, |
| char *pw, PRBool alreadyLocked, PRBool contextSpecific) |
| { |
| int len = 0; |
| CK_RV crv; |
| SECStatus rv; |
| PRTime currtime = PR_Now(); |
| PRBool mustRetry; |
| int retry = 0; |
| |
| if (slot->protectedAuthPath) { |
| len = 0; |
| pw = NULL; |
| } else if (pw == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } else { |
| len = PORT_Strlen(pw); |
| } |
| |
| do { |
| if (!alreadyLocked) |
| PK11_EnterSlotMonitor(slot); |
| crv = PK11_GETTAB(slot)->C_Login(session, |
| contextSpecific ? CKU_CONTEXT_SPECIFIC : CKU_USER, |
| (unsigned char *)pw, len); |
| slot->lastLoginCheck = 0; |
| mustRetry = PR_FALSE; |
| if (!alreadyLocked) |
| PK11_ExitSlotMonitor(slot); |
| switch (crv) { |
| /* if we're already logged in, we're good to go */ |
| case CKR_OK: |
| /* TODO If it was for CKU_CONTEXT_SPECIFIC should we do this */ |
| slot->authTransact = PK11_Global.transaction; |
| /* Fall through */ |
| case CKR_USER_ALREADY_LOGGED_IN: |
| slot->authTime = currtime; |
| rv = SECSuccess; |
| break; |
| case CKR_PIN_INCORRECT: |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
| break; |
| /* someone called reset while we fetched the password, try again once |
| * if the token is still there. */ |
| case CKR_SESSION_HANDLE_INVALID: |
| case CKR_SESSION_CLOSED: |
| if (session != slot->session) { |
| /* don't bother retrying, we were in a middle of an operation, |
| * which is now lost. Just fail. */ |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; |
| break; |
| } |
| if (retry++ == 0) { |
| rv = PK11_InitToken(slot, PR_FALSE); |
| if (rv == SECSuccess) { |
| if (slot->session != CK_INVALID_SESSION) { |
| session = slot->session; /* we should have |
| * a new session now */ |
| mustRetry = PR_TRUE; |
| } else { |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; |
| } |
| } |
| break; |
| } |
| /* Fall through */ |
| default: |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; /* some failure we can't fix by retrying */ |
| } |
| } while (mustRetry); |
| return rv; |
| } |
| |
| /* |
| * Check the user's password. Logout before hand to make sure that |
| * we are really checking the password. |
| */ |
| SECStatus |
| PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw) |
| { |
| int len = 0; |
| CK_RV crv; |
| SECStatus rv; |
| PRTime currtime = PR_Now(); |
| |
| if (slot->protectedAuthPath) { |
| len = 0; |
| pw = NULL; |
| } else if (pw == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } else { |
| len = PORT_Strlen(pw); |
| } |
| |
| /* |
| * If the token doesn't need a login, don't try to relogin because the |
| * effect is undefined. It's not clear what it means to check a non-empty |
| * password with such a token, so treat that as an error. |
| */ |
| if (!slot->needLogin) { |
| if (len == 0) { |
| rv = SECSuccess; |
| } else { |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| rv = SECFailure; |
| } |
| return rv; |
| } |
| |
| /* force a logout */ |
| PK11_EnterSlotMonitor(slot); |
| PK11_GETTAB(slot)->C_Logout(slot->session); |
| |
| crv = PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER, |
| (unsigned char *)pw, len); |
| slot->lastLoginCheck = 0; |
| PK11_ExitSlotMonitor(slot); |
| switch (crv) { |
| /* if we're already logged in, we're good to go */ |
| case CKR_OK: |
| slot->authTransact = PK11_Global.transaction; |
| slot->authTime = currtime; |
| rv = SECSuccess; |
| break; |
| case CKR_PIN_INCORRECT: |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
| break; |
| default: |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; /* some failure we can't fix by retrying */ |
| } |
| return rv; |
| } |
| |
| SECStatus |
| PK11_Logout(PK11SlotInfo *slot) |
| { |
| CK_RV crv; |
| |
| /* force a logout */ |
| PK11_EnterSlotMonitor(slot); |
| crv = PK11_GETTAB(slot)->C_Logout(slot->session); |
| slot->lastLoginCheck = 0; |
| PK11_ExitSlotMonitor(slot); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| /* |
| * transaction stuff is for when we test for the need to do every |
| * time auth to see if we already did it for this slot/transaction |
| */ |
| void |
| PK11_StartAuthTransaction(void) |
| { |
| PK11_Global.transaction++; |
| PK11_Global.inTransaction = PR_TRUE; |
| } |
| |
| void |
| PK11_EndAuthTransaction(void) |
| { |
| PK11_Global.transaction++; |
| PK11_Global.inTransaction = PR_FALSE; |
| } |
| |
| /* |
| * before we do a private key op, we check to see if we |
| * need to reauthenticate. |
| */ |
| void |
| PK11_HandlePasswordCheck(PK11SlotInfo *slot, void *wincx) |
| { |
| int askpw = slot->askpw; |
| PRBool NeedAuth = PR_FALSE; |
| |
| if (!slot->needLogin) |
| return; |
| |
| if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
| PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
| |
| if (def_slot) { |
| askpw = def_slot->askpw; |
| PK11_FreeSlot(def_slot); |
| } |
| } |
| |
| /* timeouts are handled by isLoggedIn */ |
| if (!PK11_IsLoggedIn(slot, wincx)) { |
| NeedAuth = PR_TRUE; |
| } else if (askpw == -1) { |
| if (!PK11_Global.inTransaction || |
| (PK11_Global.transaction != slot->authTransact)) { |
| PK11_EnterSlotMonitor(slot); |
| PK11_GETTAB(slot)->C_Logout(slot->session); |
| slot->lastLoginCheck = 0; |
| PK11_ExitSlotMonitor(slot); |
| NeedAuth = PR_TRUE; |
| } |
| } |
| if (NeedAuth) |
| PK11_DoPassword(slot, slot->session, PR_TRUE, |
| wincx, PR_FALSE, PR_FALSE); |
| } |
| |
| void |
| PK11_SlotDBUpdate(PK11SlotInfo *slot) |
| { |
| SECMOD_UpdateModule(slot->module); |
| } |
| |
| /* |
| * set new askpw and timeout values |
| */ |
| void |
| PK11_SetSlotPWValues(PK11SlotInfo *slot, int askpw, int timeout) |
| { |
| slot->askpw = askpw; |
| slot->timeout = timeout; |
| slot->defaultFlags |= PK11_OWN_PW_DEFAULTS; |
| PK11_SlotDBUpdate(slot); |
| } |
| |
| /* |
| * Get the askpw and timeout values for this slot |
| */ |
| void |
| PK11_GetSlotPWValues(PK11SlotInfo *slot, int *askpw, int *timeout) |
| { |
| *askpw = slot->askpw; |
| *timeout = slot->timeout; |
| |
| if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
| PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
| |
| if (def_slot) { |
| *askpw = def_slot->askpw; |
| *timeout = def_slot->timeout; |
| PK11_FreeSlot(def_slot); |
| } |
| } |
| } |
| |
| /* |
| * Returns true if the token is needLogin and isn't logged in. |
| * This function is used to determine if authentication is needed |
| * before attempting a potentially privelleged operation. |
| */ |
| PRBool |
| pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx) |
| { |
| return slot->needLogin && !PK11_IsLoggedIn(slot, wincx); |
| } |
| |
| /* |
| * make sure a slot is authenticated... |
| * This function only does the authentication if it is needed. |
| */ |
| SECStatus |
| PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) |
| { |
| if (!slot) { |
| return SECFailure; |
| } |
| if (pk11_LoginStillRequired(slot, wincx)) { |
| return PK11_DoPassword(slot, slot->session, loadCerts, wincx, |
| PR_FALSE, PR_FALSE); |
| } |
| return SECSuccess; |
| } |
| |
| /* |
| * Authenticate to "unfriendly" tokens (tokens which need to be logged |
| * in to find the certs. |
| */ |
| SECStatus |
| pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) |
| { |
| SECStatus rv = SECSuccess; |
| if (!PK11_IsFriendly(slot)) { |
| rv = PK11_Authenticate(slot, loadCerts, wincx); |
| } |
| return rv; |
| } |
| |
| /* |
| * NOTE: this assumes that we are logged out of the card before hand |
| */ |
| SECStatus |
| PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw) |
| { |
| CK_SESSION_HANDLE rwsession; |
| CK_RV crv; |
| SECStatus rv = SECFailure; |
| int len = 0; |
| |
| /* get a rwsession */ |
| rwsession = PK11_GetRWSession(slot); |
| if (rwsession == CK_INVALID_SESSION) { |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| return rv; |
| } |
| |
| if (slot->protectedAuthPath) { |
| len = 0; |
| ssopw = NULL; |
| } else if (ssopw == NULL) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } else { |
| len = PORT_Strlen(ssopw); |
| } |
| |
| /* check the password */ |
| crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO, |
| (unsigned char *)ssopw, len); |
| slot->lastLoginCheck = 0; |
| switch (crv) { |
| /* if we're already logged in, we're good to go */ |
| case CKR_OK: |
| rv = SECSuccess; |
| break; |
| case CKR_PIN_INCORRECT: |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ |
| break; |
| default: |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; /* some failure we can't fix by retrying */ |
| } |
| PK11_GETTAB(slot)->C_Logout(rwsession); |
| slot->lastLoginCheck = 0; |
| |
| /* release rwsession */ |
| PK11_RestoreROSession(slot, rwsession); |
| return rv; |
| } |
| |
| /* |
| * make sure the password conforms to your token's requirements. |
| */ |
| SECStatus |
| PK11_VerifyPW(PK11SlotInfo *slot, char *pw) |
| { |
| int len = PORT_Strlen(pw); |
| |
| if ((slot->minPassword > len) || (slot->maxPassword < len)) { |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| /* |
| * initialize a user PIN Value |
| */ |
| SECStatus |
| PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw) |
| { |
| CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION; |
| CK_RV crv; |
| SECStatus rv = SECFailure; |
| int len; |
| int ssolen; |
| |
| if (userpw == NULL) |
| userpw = ""; |
| if (ssopw == NULL) |
| ssopw = ""; |
| |
| len = PORT_Strlen(userpw); |
| ssolen = PORT_Strlen(ssopw); |
| |
| /* get a rwsession */ |
| rwsession = PK11_GetRWSession(slot); |
| if (rwsession == CK_INVALID_SESSION) { |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| slot->lastLoginCheck = 0; |
| return rv; |
| } |
| |
| if (slot->protectedAuthPath) { |
| len = 0; |
| ssolen = 0; |
| ssopw = NULL; |
| userpw = NULL; |
| } |
| |
| /* check the password */ |
| crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO, |
| (unsigned char *)ssopw, ssolen); |
| slot->lastLoginCheck = 0; |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| goto done; |
| } |
| |
| crv = PK11_GETTAB(slot)->C_InitPIN(rwsession, (unsigned char *)userpw, len); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| } else { |
| rv = SECSuccess; |
| } |
| |
| done: |
| PK11_GETTAB(slot)->C_Logout(rwsession); |
| slot->lastLoginCheck = 0; |
| PK11_RestoreROSession(slot, rwsession); |
| if (rv == SECSuccess) { |
| /* update our view of the world */ |
| PK11_InitToken(slot, PR_TRUE); |
| if (slot->needLogin) { |
| PK11_EnterSlotMonitor(slot); |
| PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER, |
| (unsigned char *)userpw, len); |
| slot->lastLoginCheck = 0; |
| PK11_ExitSlotMonitor(slot); |
| } |
| } |
| return rv; |
| } |
| |
| /* |
| * Change an existing user password |
| */ |
| SECStatus |
| PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw, const char *newpw) |
| { |
| CK_RV crv; |
| SECStatus rv = SECFailure; |
| int newLen = 0; |
| int oldLen = 0; |
| CK_SESSION_HANDLE rwsession; |
| |
| /* use NULL values to trigger the protected authentication path */ |
| if (!slot->protectedAuthPath) { |
| if (newpw == NULL) |
| newpw = ""; |
| if (oldpw == NULL) |
| oldpw = ""; |
| } |
| if (newpw) |
| newLen = PORT_Strlen(newpw); |
| if (oldpw) |
| oldLen = PORT_Strlen(oldpw); |
| |
| /* get a rwsession */ |
| rwsession = PK11_GetRWSession(slot); |
| if (rwsession == CK_INVALID_SESSION) { |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| return rv; |
| } |
| |
| crv = PK11_GETTAB(slot)->C_SetPIN(rwsession, |
| (unsigned char *)oldpw, oldLen, (unsigned char *)newpw, newLen); |
| if (crv == CKR_OK) { |
| rv = SECSuccess; |
| } else { |
| PORT_SetError(PK11_MapError(crv)); |
| } |
| |
| PK11_RestoreROSession(slot, rwsession); |
| |
| /* update our view of the world */ |
| PK11_InitToken(slot, PR_TRUE); |
| return rv; |
| } |
| |
| static char * |
| pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void *wincx) |
| { |
| if (PK11_Global.getPass == NULL) |
| return NULL; |
| return (*PK11_Global.getPass)(slot, retry, wincx); |
| } |
| |
| void |
| PK11_SetPasswordFunc(PK11PasswordFunc func) |
| { |
| PK11_Global.getPass = func; |
| } |
| |
| void |
| PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func) |
| { |
| PK11_Global.verifyPass = func; |
| } |
| |
| void |
| PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func) |
| { |
| PK11_Global.isLoggedIn = func; |
| } |
| |
| /* |
| * authenticate to a slot. This loops until we can't recover, the user |
| * gives up, or we succeed. If we're already logged in and this function |
| * is called we will still prompt for a password, but we will probably |
| * succeed no matter what the password was (depending on the implementation |
| * of the PKCS 11 module. |
| */ |
| SECStatus |
| PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session, |
| PRBool loadCerts, void *wincx, PRBool alreadyLocked, |
| PRBool contextSpecific) |
| { |
| SECStatus rv = SECFailure; |
| char *password; |
| PRBool attempt = PR_FALSE; |
| |
| if (PK11_NeedUserInit(slot)) { |
| PORT_SetError(SEC_ERROR_IO); |
| return SECFailure; |
| } |
| |
| /* |
| * Central server type applications which control access to multiple |
| * slave applications to single crypto devices need to virtuallize the |
| * login state. This is done by a callback out of PK11_IsLoggedIn and |
| * here. If we are actually logged in, then we got here because the |
| * higher level code told us that the particular client application may |
| * still need to be logged in. If that is the case, we simply tell the |
| * server code that it should now verify the clients password and tell us |
| * the results. |
| */ |
| if (PK11_IsLoggedIn(slot, NULL) && |
| (PK11_Global.verifyPass != NULL)) { |
| if (!PK11_Global.verifyPass(slot, wincx)) { |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| /* get the password. This can drop out of the while loop |
| * for the following reasons: |
| * (1) the user refused to enter a password. |
| * (return error to caller) |
| * (2) the token user password is disabled [usually due to |
| * too many failed authentication attempts]. |
| * (return error to caller) |
| * (3) the password was successful. |
| */ |
| while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) { |
| /* if the token has a protectedAuthPath, the application may have |
| * already issued the C_Login as part of it's pk11_GetPassword call. |
| * In this case the application will tell us what the results were in |
| * the password value (retry or the authentication was successful) so |
| * we can skip our own C_Login call (which would force the token to |
| * try to login again). |
| * |
| * Applications that don't know about protectedAuthPath will return a |
| * password, which we will ignore and trigger the token to |
| * 'authenticate' itself anyway. Hopefully the blinking display on |
| * the reader, or the flashing light under the thumbprint reader will |
| * attract the user's attention */ |
| attempt = PR_TRUE; |
| if (slot->protectedAuthPath) { |
| /* application tried to authenticate and failed. it wants to try |
| * again, continue looping */ |
| if (strcmp(password, PK11_PW_RETRY) == 0) { |
| rv = SECWouldBlock; |
| PORT_Free(password); |
| continue; |
| } |
| /* applicaton tried to authenticate and succeeded we're done */ |
| if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) { |
| rv = SECSuccess; |
| PORT_Free(password); |
| break; |
| } |
| } |
| rv = pk11_CheckPassword(slot, session, password, |
| alreadyLocked, contextSpecific); |
| PORT_Memset(password, 0, PORT_Strlen(password)); |
| PORT_Free(password); |
| if (rv != SECWouldBlock) |
| break; |
| } |
| if (rv == SECSuccess) { |
| if (!contextSpecific && !PK11_IsFriendly(slot)) { |
| nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, |
| slot->nssToken); |
| } |
| } else if (!attempt) |
| PORT_SetError(SEC_ERROR_BAD_PASSWORD); |
| return rv; |
| } |
| |
| void |
| PK11_LogoutAll(void) |
| { |
| SECMODListLock *lock = SECMOD_GetDefaultModuleListLock(); |
| SECMODModuleList *modList; |
| SECMODModuleList *mlp = NULL; |
| int i; |
| |
| /* NSS is not initialized, there are not tokens to log out */ |
| if (lock == NULL) { |
| return; |
| } |
| |
| SECMOD_GetReadLock(lock); |
| modList = SECMOD_GetDefaultModuleList(); |
| /* find the number of entries */ |
| for (mlp = modList; mlp != NULL; mlp = mlp->next) { |
| for (i = 0; i < mlp->module->slotCount; i++) { |
| PK11_Logout(mlp->module->slots[i]); |
| } |
| } |
| |
| SECMOD_ReleaseReadLock(lock); |
| } |
| |
| int |
| PK11_GetMinimumPwdLength(PK11SlotInfo *slot) |
| { |
| return ((int)slot->minPassword); |
| } |
| |
| /* Does this slot have a protected pin path? */ |
| PRBool |
| PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot) |
| { |
| return slot->protectedAuthPath; |
| } |
| |
| /* |
| * we can initialize the password if 1) The toke is not inited |
| * (need login == true and see need UserInit) or 2) the token has |
| * a NULL password. (slot->needLogin = false & need user Init = false). |
| */ |
| PRBool |
| PK11_NeedPWInitForSlot(PK11SlotInfo *slot) |
| { |
| if (slot->needLogin && PK11_NeedUserInit(slot)) { |
| return PR_TRUE; |
| } |
| if (!slot->needLogin && !PK11_NeedUserInit(slot)) { |
| return PR_TRUE; |
| } |
| return PR_FALSE; |
| } |
| |
| PRBool |
| PK11_NeedPWInit() |
| { |
| PK11SlotInfo *slot = PK11_GetInternalKeySlot(); |
| PRBool ret = PR_FALSE; |
| if (slot) { |
| ret = PK11_NeedPWInitForSlot(slot); |
| PK11_FreeSlot(slot); |
| } |
| return ret; |
| } |
| |
| PRBool |
| pk11_InDelayPeriod(PRIntervalTime lastTime, PRIntervalTime delayTime, |
| PRIntervalTime *retTime) |
| { |
| PRIntervalTime time; |
| |
| *retTime = time = PR_IntervalNow(); |
| return (PRBool)(lastTime) && ((time - lastTime) < delayTime); |
| } |
| |
| /* |
| * Determine if the token is logged in. We have to actually query the token, |
| * because it's state can change without intervention from us. |
| */ |
| PRBool |
| PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx) |
| { |
| CK_SESSION_INFO sessionInfo; |
| int askpw = slot->askpw; |
| int timeout = slot->timeout; |
| CK_RV crv; |
| PRIntervalTime curTime; |
| static PRIntervalTime login_delay_time = 0; |
| |
| if (login_delay_time == 0) { |
| login_delay_time = PR_SecondsToInterval(1); |
| } |
| |
| /* If we don't have our own password default values, use the system |
| * ones */ |
| if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { |
| PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); |
| |
| if (def_slot) { |
| askpw = def_slot->askpw; |
| timeout = def_slot->timeout; |
| PK11_FreeSlot(def_slot); |
| } |
| } |
| |
| if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) && |
| (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { |
| return PR_FALSE; |
| } |
| |
| /* forget the password if we've been inactive too long */ |
| if (askpw == 1) { |
| PRTime currtime = PR_Now(); |
| PRTime result; |
| PRTime mult; |
| |
| LL_I2L(result, timeout); |
| LL_I2L(mult, 60 * 1000 * 1000); |
| LL_MUL(result, result, mult); |
| LL_ADD(result, result, slot->authTime); |
| if (LL_CMP(result, <, currtime)) { |
| PK11_EnterSlotMonitor(slot); |
| PK11_GETTAB(slot)->C_Logout(slot->session); |
| slot->lastLoginCheck = 0; |
| PK11_ExitSlotMonitor(slot); |
| } else { |
| slot->authTime = currtime; |
| } |
| } |
| |
| PK11_EnterSlotMonitor(slot); |
| if (pk11_InDelayPeriod(slot->lastLoginCheck, login_delay_time, &curTime)) { |
| sessionInfo.state = slot->lastState; |
| crv = CKR_OK; |
| } else { |
| crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo); |
| if (crv == CKR_OK) { |
| slot->lastState = sessionInfo.state; |
| slot->lastLoginCheck = curTime; |
| } |
| } |
| PK11_ExitSlotMonitor(slot); |
| /* if we can't get session info, something is really wrong */ |
| if (crv != CKR_OK) { |
| slot->session = CK_INVALID_SESSION; |
| return PR_FALSE; |
| } |
| |
| switch (sessionInfo.state) { |
| case CKS_RW_PUBLIC_SESSION: |
| case CKS_RO_PUBLIC_SESSION: |
| default: |
| break; /* fail */ |
| case CKS_RW_USER_FUNCTIONS: |
| case CKS_RW_SO_FUNCTIONS: |
| case CKS_RO_USER_FUNCTIONS: |
| return PR_TRUE; |
| } |
| return PR_FALSE; |
| } |