summaryrefslogtreecommitdiff
path: root/security/nss/lib/dev/devslot.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/dev/devslot.c')
-rw-r--r--security/nss/lib/dev/devslot.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/security/nss/lib/dev/devslot.c b/security/nss/lib/dev/devslot.c
new file mode 100644
index 000000000..a113b46f5
--- /dev/null
+++ b/security/nss/lib/dev/devslot.c
@@ -0,0 +1,541 @@
+/*
+ * 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$ $Revision$ $Date$ $Name$";
+#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;
+}
+