diff options
author | Stef Walter <stef@memberwebs.com> | 2007-04-27 03:19:46 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2007-04-27 03:19:46 +0000 |
commit | 3d8ed01d2653c45e52821ba00ac72099a12600e1 (patch) | |
tree | ad13d6df465ef1a4f143109f04c35991ddb1efba /ckcapi-session.c |
Diffstat (limited to 'ckcapi-session.c')
-rw-r--r-- | ckcapi-session.c | 329 |
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")); + } +} + |