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