/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #ifdef DEBUG static const char CVS_ID[] = "@(#) $RCSfile: devslot.c,v $ $Revision: 1.2 $ $Date: 2001-12-07 01:35:53 $ $Name: not supported by cvs2svn $"; #endif /* DEBUG */ #ifndef DEV_H #include "dev.h" #endif /* DEV_H */ #ifndef DEVM_H #include "devm.h" #endif /* DEVM_H */ #ifdef NSS_3_4_CODE #include "pkcs11.h" #else #ifndef NSSCKEPV_H #include "nssckepv.h" #endif /* NSSCKEPV_H */ #endif /* NSS_3_4_CODE */ #ifndef CKHELPER_H #include "ckhelper.h" #endif /* CKHELPER_H */ #ifndef BASE_H #include "base.h" #endif /* BASE_H */ /* The flags needed to open a read-only session. */ static const CK_FLAGS s_ck_readonly_flags = CKF_SERIAL_SESSION; #ifdef PURE_STAN /* In pk11slot.c, this was a no-op. So it is here also. */ static CK_RV PR_CALLBACK nss_ck_slot_notify ( CK_SESSION_HANDLE session, CK_NOTIFICATION event, CK_VOID_PTR pData ) { return CKR_OK; } #endif /* maybe this should really inherit completely from the module... I dunno, * any uses of slots where independence is needed? */ NSS_IMPLEMENT NSSSlot * nssSlot_Create ( NSSArena *arenaOpt, CK_SLOT_ID slotID, NSSModule *parent ) { NSSArena *arena = NULL; nssArenaMark *mark = NULL; NSSSlot *rvSlot; NSSToken *token = NULL; NSSUTF8 *slotName = NULL; PRUint32 length; PRBool newArena; PRStatus nssrv; CK_SLOT_INFO slotInfo; CK_RV ckrv; if (arenaOpt) { arena = arenaOpt; mark = nssArena_Mark(arena); if (!mark) { return (NSSSlot *)NULL; } newArena = PR_FALSE; } else { arena = NSSArena_Create(); if(!arena) { return (NSSSlot *)NULL; } newArena = PR_TRUE; } rvSlot = nss_ZNEW(arena, NSSSlot); if (!rvSlot) { goto loser; } /* Get slot information */ ckrv = CKAPI(parent)->C_GetSlotInfo(slotID, &slotInfo); if (ckrv != CKR_OK) { /* set an error here, eh? */ goto loser; } /* Grab the slot description from the PKCS#11 fixed-length buffer */ length = nssPKCS11StringLength(slotInfo.slotDescription, sizeof(slotInfo.slotDescription)); if (length > 0) { slotName = nssUTF8_Create(arena, nssStringType_UTF8String, (void *)slotInfo.slotDescription, length); if (!slotName) { goto loser; } } if (!arenaOpt) { /* Avoid confusion now - only set the slot's arena to a non-NULL value * if a new arena is created. Otherwise, depend on the caller (having * passed arenaOpt) to free the arena. */ rvSlot->arena = arena; } rvSlot->refCount = 1; rvSlot->epv = parent->epv; rvSlot->module = parent; rvSlot->name = slotName; rvSlot->slotID = slotID; rvSlot->ckFlags = slotInfo.flags; /* Initialize the token if present. */ if (slotInfo.flags & CKF_TOKEN_PRESENT) { token = nssToken_Create(arena, slotID, rvSlot); if (!token) { goto loser; } } rvSlot->token = token; if (mark) { nssrv = nssArena_Unmark(arena, mark); if (nssrv != PR_SUCCESS) { goto loser; } } return rvSlot; loser: if (newArena) { nssArena_Destroy(arena); } else { if (mark) { nssArena_Release(arena, mark); } } /* everything was created in the arena, nothing to see here, move along */ return (NSSSlot *)NULL; } NSS_IMPLEMENT PRStatus nssSlot_Destroy ( NSSSlot *slot ) { if (--slot->refCount == 0) { #ifndef NSS_3_4_CODE /* Not going to do this in 3.4, maybe never */ nssToken_Destroy(slot->token); #endif if (slot->arena) { return NSSArena_Destroy(slot->arena); } else { nss_ZFreeIf(slot); } } return PR_SUCCESS; } NSS_IMPLEMENT NSSSlot * nssSlot_AddRef ( NSSSlot *slot ) { ++slot->refCount; return slot; } NSS_IMPLEMENT NSSUTF8 * nssSlot_GetName ( NSSSlot *slot, NSSArena *arenaOpt ) { if (slot->name) { return nssUTF8_Duplicate(slot->name, arenaOpt); } return (NSSUTF8 *)NULL; } static PRStatus nssslot_login(NSSSlot *slot, nssSession *session, CK_USER_TYPE userType, NSSCallback *pwcb) { PRStatus nssrv; PRUint32 attempts; PRBool keepTrying; NSSUTF8 *password = NULL; CK_ULONG pwLen; CK_RV ckrv; if (!pwcb->getPW) { /* set error INVALID_ARG */ return PR_FAILURE; } keepTrying = PR_TRUE; nssrv = PR_FAILURE; attempts = 0; while (keepTrying) { nssrv = pwcb->getPW(slot->name, &attempts, pwcb->arg, &password); if (nssrv != PR_SUCCESS) { nss_SetError(NSS_ERROR_USER_CANCELED); break; } pwLen = (CK_ULONG)nssUTF8_Length(password, &nssrv); if (nssrv != PR_SUCCESS) { break; } nssSession_EnterMonitor(session); ckrv = CKAPI(slot)->C_Login(session->handle, userType, (CK_CHAR_PTR)password, pwLen); nssSession_ExitMonitor(session); switch (ckrv) { case CKR_OK: case CKR_USER_ALREADY_LOGGED_IN: slot->authInfo.lastLogin = PR_Now(); nssrv = PR_SUCCESS; keepTrying = PR_FALSE; break; case CKR_PIN_INCORRECT: nss_SetError(NSS_ERROR_INVALID_PASSWORD); keepTrying = PR_TRUE; /* received bad pw, keep going */ break; default: nssrv = PR_FAILURE; keepTrying = PR_FALSE; break; } nss_ZFreeIf(password); password = NULL; ++attempts; } nss_ZFreeIf(password); return nssrv; } static PRStatus nssslot_init_password(NSSSlot *slot, nssSession *rwSession, NSSCallback *pwcb) { NSSUTF8 *userPW = NULL; NSSUTF8 *ssoPW = NULL; PRStatus nssrv; CK_ULONG userPWLen, ssoPWLen; CK_RV ckrv; if (!pwcb->getInitPW) { /* set error INVALID_ARG */ return PR_FAILURE; } /* Get the SO and user passwords */ nssrv = pwcb->getInitPW(slot->name, pwcb->arg, &ssoPW, &userPW); if (nssrv != PR_SUCCESS) goto loser; userPWLen = (CK_ULONG)nssUTF8_Length(userPW, &nssrv); if (nssrv != PR_SUCCESS) goto loser; ssoPWLen = (CK_ULONG)nssUTF8_Length(ssoPW, &nssrv); if (nssrv != PR_SUCCESS) goto loser; /* First log in as SO */ ckrv = CKAPI(slot)->C_Login(rwSession->handle, CKU_SO, (CK_CHAR_PTR)ssoPW, ssoPWLen); if (ckrv != CKR_OK) { /* set error ...SO_LOGIN_FAILED */ goto loser; } /* Now change the user PIN */ ckrv = CKAPI(slot)->C_InitPIN(rwSession->handle, (CK_CHAR_PTR)userPW, userPWLen); if (ckrv != CKR_OK) { /* set error */ goto loser; } nss_ZFreeIf(ssoPW); nss_ZFreeIf(userPW); return PR_SUCCESS; loser: nss_ZFreeIf(ssoPW); nss_ZFreeIf(userPW); return PR_FAILURE; } static PRStatus nssslot_change_password(NSSSlot *slot, nssSession *rwSession, NSSCallback *pwcb) { NSSUTF8 *userPW = NULL; NSSUTF8 *newPW = NULL; PRUint32 attempts; PRStatus nssrv; PRBool keepTrying = PR_TRUE; CK_ULONG userPWLen, newPWLen; CK_RV ckrv; if (!pwcb->getNewPW) { /* set error INVALID_ARG */ return PR_FAILURE; } attempts = 0; while (keepTrying) { nssrv = pwcb->getNewPW(slot->name, &attempts, pwcb->arg, &userPW, &newPW); if (nssrv != PR_SUCCESS) { nss_SetError(NSS_ERROR_USER_CANCELED); break; } userPWLen = (CK_ULONG)nssUTF8_Length(userPW, &nssrv); if (nssrv != PR_SUCCESS) return nssrv; newPWLen = (CK_ULONG)nssUTF8_Length(newPW, &nssrv); if (nssrv != PR_SUCCESS) return nssrv; nssSession_EnterMonitor(rwSession); ckrv = CKAPI(slot)->C_SetPIN(rwSession->handle, (CK_CHAR_PTR)userPW, userPWLen, (CK_CHAR_PTR)newPW, newPWLen); nssSession_ExitMonitor(rwSession); switch (ckrv) { case CKR_OK: slot->authInfo.lastLogin = PR_Now(); nssrv = PR_SUCCESS; keepTrying = PR_FALSE; break; case CKR_PIN_INCORRECT: nss_SetError(NSS_ERROR_INVALID_PASSWORD); keepTrying = PR_TRUE; /* received bad pw, keep going */ break; default: nssrv = PR_FAILURE; keepTrying = PR_FALSE; break; } nss_ZFreeIf(userPW); nss_ZFreeIf(newPW); userPW = NULL; newPW = NULL; ++attempts; } nss_ZFreeIf(userPW); nss_ZFreeIf(newPW); return nssrv; } NSS_IMPLEMENT PRStatus nssSlot_Login ( NSSSlot *slot, PRBool asSO, NSSCallback *pwcb ) { PRBool needsLogin, needsInit; CK_USER_TYPE userType; userType = (asSO) ? CKU_SO : CKU_USER; needsInit = PR_FALSE; /* XXX */ needsLogin = PR_TRUE; /* XXX */ if (needsInit) { return nssSlot_SetPassword(slot, pwcb); } else if (needsLogin) { return nssslot_login(slot, slot->token->defaultSession, userType, pwcb); } return PR_SUCCESS; /* login not required */ } NSS_IMPLEMENT PRStatus nssSlot_Logout ( NSSSlot *slot, nssSession *sessionOpt ) { nssSession *session; PRStatus nssrv = PR_SUCCESS; CK_RV ckrv; session = (sessionOpt) ? sessionOpt : slot->token->defaultSession; nssSession_EnterMonitor(session); ckrv = CKAPI(slot)->C_Logout(session->handle); nssSession_ExitMonitor(session); if (ckrv != CKR_OK) { /* translate the error */ nssrv = PR_FAILURE; } return nssrv; } NSS_IMPLEMENT void nssSlot_SetPasswordDefaults ( NSSSlot *slot, PRInt32 askPasswordTimeout ) { slot->authInfo.askPasswordTimeout = askPasswordTimeout; } NSS_IMPLEMENT PRStatus nssSlot_SetPassword ( NSSSlot *slot, NSSCallback *pwcb ) { PRStatus nssrv; nssSession *rwSession; PRBool needsInit; needsInit = PR_FALSE; /* XXX */ rwSession = nssSlot_CreateSession(slot, NULL, PR_TRUE); if (needsInit) { nssrv = nssslot_init_password(slot, rwSession, pwcb); } else { nssrv = nssslot_change_password(slot, rwSession, pwcb); } nssSession_Destroy(rwSession); return nssrv; } #ifdef PURE_STAN NSS_IMPLEMENT nssSession * nssSlot_CreateSession ( NSSSlot *slot, NSSArena *arenaOpt, PRBool readWrite /* so far, this is the only flag used */ ) { CK_RV ckrv; CK_FLAGS ckflags; CK_SESSION_HANDLE session; nssSession *rvSession; ckflags = s_ck_readonly_flags; if (readWrite) { ckflags |= CKF_RW_SESSION; } /* does the opening and closing of sessions need to be done in a * threadsafe manner? should there be a "meta-lock" controlling * calls like this? */ ckrv = CKAPI(slot)->C_OpenSession(slot->slotID, ckflags, slot, nss_ck_slot_notify, &session); if (ckrv != CKR_OK) { /* set an error here, eh? */ return (nssSession *)NULL; } rvSession = nss_ZNEW(arenaOpt, nssSession); if (!rvSession) { return (nssSession *)NULL; } if (slot->module->flags & NSSMODULE_FLAGS_NOT_THREADSAFE) { /* If the parent module is not threadsafe, create lock to manage * session within threads. */ rvSession->lock = PZ_NewLock(nssILockOther); if (!rvSession->lock) { /* need to translate NSPR error? */ if (arenaOpt) { } else { nss_ZFreeIf(rvSession); } return (nssSession *)NULL; } } rvSession->handle = session; rvSession->slot = slot; rvSession->isRW = readWrite; return rvSession; } NSS_IMPLEMENT PRStatus nssSession_Destroy ( nssSession *s ) { CK_RV ckrv = CKR_OK; if (s) { ckrv = CKAPI(s->slot)->C_CloseSession(s->handle); if (s->lock) { PZ_DestroyLock(s->lock); } nss_ZFreeIf(s); } return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; } #endif NSS_IMPLEMENT PRStatus nssSession_EnterMonitor ( nssSession *s ) { if (s->lock) PZ_Lock(s->lock); return PR_SUCCESS; } NSS_IMPLEMENT PRStatus nssSession_ExitMonitor ( nssSession *s ) { return (s->lock) ? PZ_Unlock(s->lock) : PR_SUCCESS; } NSS_EXTERN PRBool nssSession_IsReadWrite ( nssSession *s ) { return s->isRW; }