Blame nss/lib/pk11wrap/pk11auth.c

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