summaryrefslogtreecommitdiff
path: root/ckcapi-session.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2007-04-27 03:19:46 +0000
committerStef Walter <stef@memberwebs.com>2007-04-27 03:19:46 +0000
commit3d8ed01d2653c45e52821ba00ac72099a12600e1 (patch)
treead13d6df465ef1a4f143109f04c35991ddb1efba /ckcapi-session.c
Diffstat (limited to 'ckcapi-session.c')
-rw-r--r--ckcapi-session.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/ckcapi-session.c b/ckcapi-session.c
new file mode 100644
index 0000000..dc700c1
--- /dev/null
+++ b/ckcapi-session.c
@@ -0,0 +1,329 @@
+
+#include <stdlib.h>
+
+#include "cryptoki-capi.h"
+
+typedef struct _SessionList {
+ Session **list;
+ size_t lmax;
+} SessionList;
+
+/* These are protected by global_mutex */
+static SessionList the_sessions = { NULL, 0 };
+
+
+Session*
+ckcapi_session_create(void)
+{
+ Session* sess = calloc(1, sizeof(Session));
+ if(!sess)
+ return NULL;
+
+ sess->mutex = CreateMutex(NULL, FALSE, NULL);
+ if(!sess->mutex)
+ {
+ free(sess);
+ return NULL;
+ }
+
+ DBGS(sess, "created");
+ return sess;
+}
+
+CK_RV
+ckcapi_session_register(Session* sess)
+{
+ CK_ULONG id = 0;
+ CK_RV ret = CKR_OK;
+ size_t i;
+
+ ASSERT(sess);
+ ASSERT(sess->id == 0 && sess->refs == 0);
+
+ DBGS(sess, "registering new session");
+
+ ckcapi_lock_global();
+
+ /* Find a nice session identifier */
+ while(id == 0) {
+
+ /*
+ * PKCS#11 GRAY AREA: We're assuming we can reuse session
+ * handles. PKCS#11 spec says they're like file handles,
+ * and file handles get reused :)
+ */
+
+ /* Note we never put anything in array position '0' */
+ for(i = 1; i < the_sessions.lmax; ++i)
+ {
+ /* Any empty position will do */
+ if(!the_sessions.list[i])
+ {
+ id = i;
+ break;
+ }
+ }
+
+ /* Couldn't find a handle, reallocate */
+ if(id == 0)
+ {
+ Session** buf;
+ size_t oldmax, newmax;
+
+ oldmax = the_sessions.lmax;
+ newmax = oldmax + 16;
+
+ buf = realloc(the_sessions.list, newmax * sizeof(Session*));
+ if(!buf)
+ {
+ DBGS(sess, ("couldn't allocate session list, out of memory"));
+ ret = CKR_HOST_MEMORY;
+ break;
+ }
+
+ /* Choose the first of the new block as the id */
+ id = oldmax;
+
+ /* Clear new memory */
+ the_sessions.list = buf;
+ for( ; oldmax < newmax; ++oldmax)
+ buf[oldmax] = NULL;
+ the_sessions.lmax = newmax;
+
+ DBG(("allocated new session list: %d max", newmax));
+ }
+ }
+
+ if(ret == CKR_OK)
+ {
+ ASSERT(id > 0 && id < the_sessions.lmax);
+ ASSERT(the_sessions.list[id] == NULL);
+
+ /* And assign it to the session handle */
+ the_sessions.list[id] = sess;
+ sess->id = id;
+
+ /* The session list reference */
+ ASSERT(sess->refs == 0);
+ sess->refs++;
+
+ DBGS(sess, "registered sesson id");
+ }
+
+ ckcapi_unlock_global();
+
+ return ret;
+}
+
+void
+ckcapi_session_destroy(Session* sess)
+{
+ ASSERT(sess);
+ ASSERT(sess->refs == 0);
+
+ /* Ask any pending operations to cleanup */
+ if(sess->operation_type)
+ {
+ ASSERT(sess->operation_cancel);
+ (sess->operation_cancel)(sess);
+ }
+
+ ASSERT(sess->operation_type == 0);
+ ASSERT(sess->operation_data == NULL);
+ ASSERT(sess->operation_cancel == NULL);
+
+ /* And make the mutex go away */
+ ASSERT(sess->mutex != NULL);
+ CloseHandle(sess->mutex);
+
+ DBGS(sess, "destroyed");
+ free(sess);
+}
+
+static CK_RV
+find_lock_ref_internal(SessionList* sessions, CK_SESSION_HANDLE id,
+ int remove, Session** sess_ret)
+{
+ Session *sess;
+ DWORD r;
+
+ ASSERT(sessions);
+ ASSERT(sess_ret);
+
+ if(id >= sessions->lmax)
+ {
+ DBG(("invalid session id: %d", id));
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /* A seemingly valid id */
+ ASSERT(sessions->list);
+ sess = sessions->list[id];
+
+ if(!sess)
+ {
+ DBG(("session does not exist: %d", id));
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ ASSERT(sess->id == id);
+
+ /* Closing takes precedence over active operations */
+ if(!remove)
+ {
+ /*
+ * An initial check is done to make sure this session is not active.
+ * This is done outside of the lock. The real check is done later
+ * inside a lock. This is so we can return quickly without blocking
+ * in most cases.
+ */
+
+ if(sess->in_call)
+ {
+ DBGS(sess, ("an operation is already active in this session"));
+ return CKR_OPERATION_ACTIVE;
+ }
+ }
+
+ /* Lock the CallSession */
+ r = WaitForSingleObject(sess->mutex, INFINITE);
+ ASSERT(r == WAIT_OBJECT_0);
+
+ /* Do the real check */
+ if(!remove && sess->in_call)
+ {
+ ReleaseMutex(sess->mutex);
+ DBGS(sess, ("an operation is already active in this session"));
+ return CKR_OPERATION_ACTIVE;
+ }
+
+ /* Make sure it doesn't go away */
+ ASSERT(sess->refs > 0);
+ sess->refs++;
+
+ DBGS(sess, "found and locked session");
+
+ /* And remove it if necessary */
+ if(remove)
+ {
+ sessions->list[id] = NULL;
+
+ /* The session list reference */
+ sess->refs--;
+ ASSERT(sess->refs > 0);
+
+ DBGS(sess, "removed session from list");
+ }
+ else
+ {
+ ASSERT(!sess->in_call);
+ sess->in_call = 1;
+ }
+
+ *sess_ret = sess;
+ return CKR_OK;
+}
+
+CK_RV
+ckcapi_session_find_lock_ref(CK_ULONG id, int remove, Session **sess)
+{
+ /* This must be called without any locks held */
+
+ CK_RV ret = CKR_OK;
+
+ ASSERT(sess);
+
+ if(id <= 0)
+ {
+ DBG(("invalid session id passed: %d", id));
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ ckcapi_lock_global();
+
+ ret = find_lock_ref_internal (&the_sessions, id, remove, sess);
+
+ ckcapi_unlock_global();
+
+ return ret;
+}
+
+void
+ckcapi_session_unref_unlock(Session* sess)
+{
+ /* The CallSession must be locked at this point */
+
+ int refs;
+ BOOL r;
+
+ ASSERT(sess);
+
+ ASSERT(sess->refs > 0);
+ sess->refs--;
+ refs = sess->refs;
+
+ sess->in_call = 0;
+
+ DBGS(sess, "unlocked session");
+
+ r = ReleaseMutex(sess->mutex);
+ ASSERT(r == TRUE);
+
+ /*
+ * At this point if no references are held, then we can safely
+ * delete. No other thread should be involved.
+ */
+
+ if(refs == 0)
+ ckcapi_session_destroy(sess);
+}
+
+void
+ckcapi_session_close_all()
+{
+ /* This must be called without any locks held */
+
+ SessionList sessions;
+ Session *sess;
+ size_t i;
+ CK_RV ret;
+
+ /*
+ * PKCS#11 GRAY AREA: What happens when this gets called
+ * concurrently? We don't return an error on the second call,
+ * because by the time it returns, all sessions should be closed.
+ */
+
+ ckcapi_lock_global();
+
+ /* Steal all the session data */
+ sessions.list = the_sessions.list;
+ the_sessions.list = NULL;
+ sessions.lmax = the_sessions.lmax;
+ the_sessions.lmax = 0;
+
+ if(sessions.list || sessions.lmax)
+ DBG(("closing all sessions"));
+
+ ckcapi_unlock_global();
+
+ /* Close each session in turn */
+ for(i = 1; i < sessions.lmax; ++i)
+ {
+ if(!sessions.list[i])
+ continue;
+
+ ret = find_lock_ref_internal (&sessions, i, 1, &sess);
+ ASSERT(ret == CKR_OK);
+
+ ckcapi_session_unref_unlock(sess);
+ }
+
+ /* We stole the memory above, free it now */
+ if(sessions.list)
+ {
+ free(sessions.list);
+ DBG(("freed session list"));
+ }
+}
+