/* * pkcs11-session.c: PKCS#11 functions for session management * * Copyright (C) 2001 Timo Teräs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "sc-pkcs11.h" CK_RV get_session(CK_SESSION_HANDLE hSession, struct sc_pkcs11_session **session) { *session = list_seek(&sessions, &hSession); if (!*session) return CKR_SESSION_HANDLE_INVALID; return CKR_OK; } CK_RV C_OpenSession(CK_SLOT_ID slotID, /* the slot's ID */ CK_FLAGS flags, /* defined in CK_SESSION_INFO */ CK_VOID_PTR pApplication, /* pointer passed to callback */ CK_NOTIFY Notify, /* notification callback function */ CK_SESSION_HANDLE_PTR phSession) { /* receives new session handle */ CK_RV rv; struct sc_pkcs11_slot *slot; struct sc_pkcs11_session *session; if (!(flags & CKF_SERIAL_SESSION)) return CKR_SESSION_PARALLEL_NOT_SUPPORTED; if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION)) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_OpenSession(0x%lx)", slotID); rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; /* Check that no conflictions sessions exist */ if (!(flags & CKF_RW_SESSION) && (slot->login_user == CKU_SO)) { rv = CKR_SESSION_READ_WRITE_SO_EXISTS; goto out; } session = (struct sc_pkcs11_session *)calloc(1, sizeof(struct sc_pkcs11_session)); if (session == NULL) { rv = CKR_HOST_MEMORY; goto out; } session->slot = slot; session->notify_callback = Notify; session->notify_data = pApplication; session->flags = flags; slot->nsessions++; session->handle = (CK_SESSION_HANDLE) session; /* cast a pointer to long */ list_append(&sessions, session); *phSession = session->handle; sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_OpenSession handle: 0x%lx", session->handle); out: sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_OpenSession() = %s", lookup_enum(RV_T, rv)); sc_pkcs11_unlock(); return rv; } /* Internal version of C_CloseSession that gets called with * the global lock held */ static CK_RV sc_pkcs11_close_session(CK_SESSION_HANDLE hSession) { struct sc_pkcs11_slot *slot; struct sc_pkcs11_session *session; sc_debug(context, SC_LOG_DEBUG_NORMAL, "real C_CloseSession(0x%lx)", hSession); session = list_seek(&sessions, &hSession); if (!session) return CKR_SESSION_HANDLE_INVALID; /* If we're the last session using this slot, make sure * we log out */ slot = session->slot; slot->nsessions--; if (slot->nsessions == 0 && slot->login_user >= 0) { slot->login_user = -1; slot->card->framework->logout(slot->card, slot->fw_data); } if (list_delete(&sessions, session) != 0) sc_debug(context, SC_LOG_DEBUG_NORMAL, "Could not delete session from list!"); free(session); return CKR_OK; } /* Internal version of C_CloseAllSessions that gets called with * the global lock held */ CK_RV sc_pkcs11_close_all_sessions(CK_SLOT_ID slotID) { CK_RV rv = CKR_OK; struct sc_pkcs11_session *session; unsigned int i; sc_debug(context, SC_LOG_DEBUG_NORMAL, "real C_CloseAllSessions(0x%lx) %d", slotID, list_size(&sessions)); for (i = 0; i < list_size(&sessions); i++) { session = list_get_at(&sessions, i); if (session->slot->id == slotID) if ((rv = sc_pkcs11_close_session(session->handle)) != CKR_OK) return rv; } return CKR_OK; } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { /* the session's handle */ CK_RV rv; sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_CloseSession(0x%lx)\n", hSession); rv = sc_pkcs11_lock(); if (rv == CKR_OK) rv = sc_pkcs11_close_session(hSession); sc_pkcs11_unlock(); return rv; } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { /* the token's slot */ CK_RV rv; struct sc_pkcs11_slot *slot; sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_CloseAllSessions(0x%lx)\n", slotID); rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; rv = sc_pkcs11_close_all_sessions(slotID); out:sc_pkcs11_unlock(); return rv; } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_SESSION_INFO_PTR pInfo) { /* receives session information */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_GetSessionInfo(0x%lx)", hSession); session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_GetSessionInfo(slot 0x%lx).", session->slot->id); pInfo->slotID = session->slot->id; pInfo->flags = session->flags; pInfo->ulDeviceError = 0; slot = session->slot; if (slot->login_user == CKU_SO) { pInfo->state = CKS_RW_SO_FUNCTIONS; } else if (slot->login_user == CKU_USER || (!(slot->token_info.flags & CKF_LOGIN_REQUIRED))) { pInfo->state = (session->flags & CKF_RW_SESSION) ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS; } else { pInfo->state = (session->flags & CKF_RW_SESSION) ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION; } out: sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_GetSessionInfo(0x%lx) = %s", hSession, lookup_enum(RV_T, rv)); sc_pkcs11_unlock(); return rv; } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pOperationState, /* location receiving state */ CK_ULONG_PTR pulOperationStateLen) { /* location receiving state length */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pOperationState, /* the location holding the state */ CK_ULONG ulOperationStateLen, /* location holding state length */ CK_OBJECT_HANDLE hEncryptionKey, /* handle of en/decryption key */ CK_OBJECT_HANDLE hAuthenticationKey) { /* handle of sign/verify key */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_USER_TYPE userType, /* the user type */ CK_CHAR_PTR pPin, /* the user's PIN */ CK_ULONG ulPinLen) { /* the length of the PIN */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if (pPin == NULL_PTR && ulPinLen > 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (userType != CKU_USER && userType != CKU_SO && userType != CKU_CONTEXT_SPECIFIC) { rv = CKR_USER_TYPE_INVALID; goto out; } session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_Login(0x%lx, %d)", hSession, userType); slot = session->slot; if (!(slot->token_info.flags & CKF_USER_PIN_INITIALIZED)) { rv = CKR_USER_PIN_NOT_INITIALIZED; goto out; } /* TODO: check if context specific is valid */ if (userType == CKU_CONTEXT_SPECIFIC) { if (slot->login_user == -1) { rv = CKR_OPERATION_NOT_INITIALIZED; goto out; } else rv = slot->card->framework->login(slot, userType, pPin, ulPinLen); } else { if (slot->login_user >= 0) { if ((CK_USER_TYPE) slot->login_user == userType) rv = CKR_USER_ALREADY_LOGGED_IN; else rv = CKR_USER_ANOTHER_ALREADY_LOGGED_IN; goto out; } rv = slot->card->framework->login(slot, userType, pPin, ulPinLen); if (rv == CKR_OK) slot->login_user = userType; } out:sc_pkcs11_unlock(); return rv; } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { /* the session's handle */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_debug(context, SC_LOG_DEBUG_NORMAL, "C_Logout(0x%lx)", hSession); slot = session->slot; if (slot->login_user >= 0) { slot->login_user = -1; rv = slot->card->framework->logout(slot->card, slot->fw_data); } else rv = CKR_USER_NOT_LOGGED_IN; out:sc_pkcs11_unlock(); return rv; } CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if (pPin == NULL_PTR && ulPinLen > 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } slot = session->slot; if (slot->login_user != CKU_SO) { rv = CKR_USER_NOT_LOGGED_IN; } else if (slot->card->framework->init_pin == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; } else { rv = slot->card->framework->init_pin(slot->card, slot, pPin, ulPinLen); } out:sc_pkcs11_unlock(); return rv; } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen) { CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if ((pOldPin == NULL_PTR && ulOldLen > 0) || (pNewPin == NULL_PTR && ulNewLen > 0)) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } slot = session->slot; sc_debug(context, SC_LOG_DEBUG_NORMAL, "Changing PIN (session 0x%lx; login user %d)\n", hSession, slot->login_user); if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } rv = slot->card->framework->change_pin(slot->card, slot->fw_data, slot->login_user, pOldPin, ulOldLen, pNewPin, ulNewLen); out:sc_pkcs11_unlock(); return rv; }