#ifdef HAVE_UNISTD_H # include <unistd.h> #endif #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif #ifdef HAVE_STRING_H # include <string.h> #endif #ifdef HAVE_STRINGS_H # include <strings.h> #endif #ifdef HAVE_DLFCN_H # include <dlfcn.h> #endif #ifdef HAVE_DL_H # include <dl.h> #endif #ifdef _WIN32 # include <windows.h> #endif #include <tcl.h> #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION >= 86 # define TCL_INCLUDES_LOADFILE 1 #endif /* PKCS#11 Definitions for the local platform */ #ifndef _WIN32 # define CK_PTR * # define CK_DECLARE_FUNCTION(rv, func) rv func # define CK_DECLARE_FUNCTION_POINTER(rv, func) rv (CK_PTR func) # define CK_CALLBACK_FUNCTION(rv, func) rv (CK_PTR func) # define CK_NULL_PTR ((void *) 0) #else # define CK_PTR * # define CK_DECLARE_FUNCTION(rv, func) rv __declspec(dllimport) func # define CK_DECLARE_FUNCTION_POINTER(rv, func) rv __declspec(dllimport) (CK_PTR func) # define CK_CALLBACK_FUNCTION(rv, func) rv (CK_PTR func) # define CK_NULL_PTR ((void *) 0) # pragma pack(push, cryptoki, 1) #endif #include "pkcs11.h" #ifdef _WIN32 # pragma pack(pop, cryptoki) #endif struct tclpkcs11_interpdata { /* Handle Hash Table */ Tcl_HashTable handles; unsigned long handles_idx; }; struct tclpkcs11_handle { /* PKCS11 Module Pointers */ void *base; CK_FUNCTION_LIST_PTR pkcs11; /* Session Management */ CK_SLOT_ID session_slot; CK_SESSION_HANDLE session; }; /* * Tcl <--> PKCS11 Bridge Functions */ MODULE_SCOPE Tcl_Obj *tclpkcs11_pkcs11_error(CK_RV errorCode) { switch (errorCode) { case CKR_OK: return(Tcl_NewStringObj("PKCS11_OK OK", -1)); case CKR_CANCEL: return(Tcl_NewStringObj("PKCS11_ERROR CANCEL", -1)); case CKR_HOST_MEMORY: return(Tcl_NewStringObj("PKCS11_ERROR HOST_MEMORY", -1)); case CKR_SLOT_ID_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR SLOT_ID_INVALID", -1)); case CKR_GENERAL_ERROR: return(Tcl_NewStringObj("PKCS11_ERROR GENERAL_ERROR", -1)); case CKR_FUNCTION_FAILED: return(Tcl_NewStringObj("PKCS11_ERROR FUNCTION_FAILED", -1)); case CKR_ARGUMENTS_BAD: return(Tcl_NewStringObj("PKCS11_ERROR ARGUMENTS_BAD", -1)); case CKR_NO_EVENT: return(Tcl_NewStringObj("PKCS11_ERROR NO_EVENT", -1)); case CKR_NEED_TO_CREATE_THREADS: return(Tcl_NewStringObj("PKCS11_ERROR NEED_TO_CREATE_THREADS", -1)); case CKR_CANT_LOCK: return(Tcl_NewStringObj("PKCS11_ERROR CANT_LOCK", -1)); case CKR_ATTRIBUTE_READ_ONLY: return(Tcl_NewStringObj("PKCS11_ERROR ATTRIBUTE_READ_ONLY", -1)); case CKR_ATTRIBUTE_SENSITIVE: return(Tcl_NewStringObj("PKCS11_ERROR ATTRIBUTE_SENSITIVE", -1)); case CKR_ATTRIBUTE_TYPE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR ATTRIBUTE_TYPE_INVALID", -1)); case CKR_ATTRIBUTE_VALUE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR ATTRIBUTE_VALUE_INVALID", -1)); case CKR_DATA_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR DATA_INVALID", -1)); case CKR_DATA_LEN_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR DATA_LEN_RANGE", -1)); case CKR_DEVICE_ERROR: return(Tcl_NewStringObj("PKCS11_ERROR DEVICE_ERROR", -1)); case CKR_DEVICE_MEMORY: return(Tcl_NewStringObj("PKCS11_ERROR DEVICE_MEMORY", -1)); case CKR_DEVICE_REMOVED: return(Tcl_NewStringObj("PKCS11_ERROR DEVICE_REMOVED", -1)); case CKR_ENCRYPTED_DATA_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR ENCRYPTED_DATA_INVALID", -1)); case CKR_ENCRYPTED_DATA_LEN_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR ENCRYPTED_DATA_LEN_RANGE", -1)); case CKR_FUNCTION_CANCELED: return(Tcl_NewStringObj("PKCS11_ERROR FUNCTION_CANCELED", -1)); case CKR_FUNCTION_NOT_PARALLEL: return(Tcl_NewStringObj("PKCS11_ERROR FUNCTION_NOT_PARALLEL", -1)); case CKR_FUNCTION_NOT_SUPPORTED: return(Tcl_NewStringObj("PKCS11_ERROR FUNCTION_NOT_SUPPORTED", -1)); case CKR_KEY_HANDLE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR KEY_HANDLE_INVALID", -1)); case CKR_KEY_SIZE_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR KEY_SIZE_RANGE", -1)); case CKR_KEY_TYPE_INCONSISTENT: return(Tcl_NewStringObj("PKCS11_ERROR KEY_TYPE_INCONSISTENT", -1)); case CKR_KEY_NOT_NEEDED: return(Tcl_NewStringObj("PKCS11_ERROR KEY_NOT_NEEDED", -1)); case CKR_KEY_CHANGED: return(Tcl_NewStringObj("PKCS11_ERROR KEY_CHANGED", -1)); case CKR_KEY_NEEDED: return(Tcl_NewStringObj("PKCS11_ERROR KEY_NEEDED", -1)); case CKR_KEY_INDIGESTIBLE: return(Tcl_NewStringObj("PKCS11_ERROR KEY_INDIGESTIBLE", -1)); case CKR_KEY_FUNCTION_NOT_PERMITTED: return(Tcl_NewStringObj("PKCS11_ERROR KEY_FUNCTION_NOT_PERMITTED", -1)); case CKR_KEY_NOT_WRAPPABLE: return(Tcl_NewStringObj("PKCS11_ERROR KEY_NOT_WRAPPABLE", -1)); case CKR_KEY_UNEXTRACTABLE: return(Tcl_NewStringObj("PKCS11_ERROR KEY_UNEXTRACTABLE", -1)); case CKR_MECHANISM_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR MECHANISM_INVALID", -1)); case CKR_MECHANISM_PARAM_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR MECHANISM_PARAM_INVALID", -1)); case CKR_OBJECT_HANDLE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR OBJECT_HANDLE_INVALID", -1)); case CKR_OPERATION_ACTIVE: return(Tcl_NewStringObj("PKCS11_ERROR OPERATION_ACTIVE", -1)); case CKR_OPERATION_NOT_INITIALIZED: return(Tcl_NewStringObj("PKCS11_ERROR OPERATION_NOT_INITIALIZED", -1)); case CKR_PIN_INCORRECT: return(Tcl_NewStringObj("PKCS11_ERROR PIN_INCORRECT", -1)); case CKR_PIN_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR PIN_INVALID", -1)); case CKR_PIN_LEN_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR PIN_LEN_RANGE", -1)); case CKR_PIN_EXPIRED: return(Tcl_NewStringObj("PKCS11_ERROR PIN_EXPIRED", -1)); case CKR_PIN_LOCKED: return(Tcl_NewStringObj("PKCS11_ERROR PIN_LOCKED", -1)); case CKR_SESSION_CLOSED: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_CLOSED", -1)); case CKR_SESSION_COUNT: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_COUNT", -1)); case CKR_SESSION_HANDLE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_HANDLE_INVALID", -1)); case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_PARALLEL_NOT_SUPPORTED", -1)); case CKR_SESSION_READ_ONLY: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_READ_ONLY", -1)); case CKR_SESSION_EXISTS: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_EXISTS", -1)); case CKR_SESSION_READ_ONLY_EXISTS: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_READ_ONLY_EXISTS", -1)); case CKR_SESSION_READ_WRITE_SO_EXISTS: return(Tcl_NewStringObj("PKCS11_ERROR SESSION_READ_WRITE_SO_EXISTS", -1)); case CKR_SIGNATURE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR SIGNATURE_INVALID", -1)); case CKR_SIGNATURE_LEN_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR SIGNATURE_LEN_RANGE", -1)); case CKR_TEMPLATE_INCOMPLETE: return(Tcl_NewStringObj("PKCS11_ERROR TEMPLATE_INCOMPLETE", -1)); case CKR_TEMPLATE_INCONSISTENT: return(Tcl_NewStringObj("PKCS11_ERROR TEMPLATE_INCONSISTENT", -1)); case CKR_TOKEN_NOT_PRESENT: return(Tcl_NewStringObj("PKCS11_ERROR TOKEN_NOT_PRESENT", -1)); case CKR_TOKEN_NOT_RECOGNIZED: return(Tcl_NewStringObj("PKCS11_ERROR TOKEN_NOT_RECOGNIZED", -1)); case CKR_TOKEN_WRITE_PROTECTED: return(Tcl_NewStringObj("PKCS11_ERROR TOKEN_WRITE_PROTECTED", -1)); case CKR_UNWRAPPING_KEY_HANDLE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR UNWRAPPING_KEY_HANDLE_INVALID", -1)); case CKR_UNWRAPPING_KEY_SIZE_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR UNWRAPPING_KEY_SIZE_RANGE", -1)); case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: return(Tcl_NewStringObj("PKCS11_ERROR UNWRAPPING_KEY_TYPE_INCONSISTENT", -1)); case CKR_USER_ALREADY_LOGGED_IN: return(Tcl_NewStringObj("PKCS11_ERROR USER_ALREADY_LOGGED_IN", -1)); case CKR_USER_NOT_LOGGED_IN: return(Tcl_NewStringObj("PKCS11_ERROR USER_NOT_LOGGED_IN", -1)); case CKR_USER_PIN_NOT_INITIALIZED: return(Tcl_NewStringObj("PKCS11_ERROR USER_PIN_NOT_INITIALIZED", -1)); case CKR_USER_TYPE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR USER_TYPE_INVALID", -1)); case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: return(Tcl_NewStringObj("PKCS11_ERROR USER_ANOTHER_ALREADY_LOGGED_IN", -1)); case CKR_USER_TOO_MANY_TYPES: return(Tcl_NewStringObj("PKCS11_ERROR USER_TOO_MANY_TYPES", -1)); case CKR_WRAPPED_KEY_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR WRAPPED_KEY_INVALID", -1)); case CKR_WRAPPED_KEY_LEN_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR WRAPPED_KEY_LEN_RANGE", -1)); case CKR_WRAPPING_KEY_HANDLE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR WRAPPING_KEY_HANDLE_INVALID", -1)); case CKR_WRAPPING_KEY_SIZE_RANGE: return(Tcl_NewStringObj("PKCS11_ERROR WRAPPING_KEY_SIZE_RANGE", -1)); case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: return(Tcl_NewStringObj("PKCS11_ERROR WRAPPING_KEY_TYPE_INCONSISTENT", -1)); case CKR_RANDOM_SEED_NOT_SUPPORTED: return(Tcl_NewStringObj("PKCS11_ERROR RANDOM_SEED_NOT_SUPPORTED", -1)); case CKR_RANDOM_NO_RNG: return(Tcl_NewStringObj("PKCS11_ERROR RANDOM_NO_RNG", -1)); case CKR_DOMAIN_PARAMS_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR DOMAIN_PARAMS_INVALID", -1)); case CKR_BUFFER_TOO_SMALL: return(Tcl_NewStringObj("PKCS11_ERROR BUFFER_TOO_SMALL", -1)); case CKR_SAVED_STATE_INVALID: return(Tcl_NewStringObj("PKCS11_ERROR SAVED_STATE_INVALID", -1)); case CKR_INFORMATION_SENSITIVE: return(Tcl_NewStringObj("PKCS11_ERROR INFORMATION_SENSITIVE", -1)); case CKR_STATE_UNSAVEABLE: return(Tcl_NewStringObj("PKCS11_ERROR STATE_UNSAVEABLE", -1)); case CKR_CRYPTOKI_NOT_INITIALIZED: return(Tcl_NewStringObj("PKCS11_ERROR CRYPTOKI_NOT_INITIALIZED", -1)); case CKR_CRYPTOKI_ALREADY_INITIALIZED: return(Tcl_NewStringObj("PKCS11_ERROR CRYPTOKI_ALREADY_INITIALIZED", -1)); case CKR_MUTEX_BAD: return(Tcl_NewStringObj("PKCS11_ERROR MUTEX_BAD", -1)); case CKR_MUTEX_NOT_LOCKED: return(Tcl_NewStringObj("PKCS11_ERROR MUTEX_NOT_LOCKED", -1)); case CKR_NEW_PIN_MODE: return(Tcl_NewStringObj("PKCS11_ERROR NEW_PIN_MODE", -1)); case CKR_NEXT_OTP: return(Tcl_NewStringObj("PKCS11_ERROR NEXT_OTP", -1)); case CKR_FUNCTION_REJECTED: return(Tcl_NewStringObj("PKCS11_ERROR FUNCTION_REJECTED", -1)); case CKR_VENDOR_DEFINED: return(Tcl_NewStringObj("PKCS11_ERROR VENDOR_DEFINED", -1)); } return(Tcl_NewStringObj("PKCS11_ERROR UNKNOWN", -1)); } MODULE_SCOPE Tcl_Obj *tclpkcs11_bytearray_to_string(const unsigned char *data, unsigned long datalen) { static char alphabet[] = "0123456789abcdef"; unsigned long idx, bufidx; Tcl_Obj *retval; char buf[1024]; if (data == NULL) { return(Tcl_NewObj()); } for (bufidx = idx = 0; (idx < datalen) && (bufidx < sizeof(buf)); idx++) { buf[bufidx++] = alphabet[(data[idx] >> 4) & 0xf]; buf[bufidx++] = alphabet[data[idx] & 0xf]; } retval = Tcl_NewByteArrayObj((unsigned char *) buf, bufidx); return(retval); } MODULE_SCOPE unsigned long tclpkcs11_string_to_bytearray(Tcl_Obj *data, unsigned char *outbuf, unsigned long outbuflen) { unsigned long outbufidx = 0; char tmpbuf[5]; char *str; int tmpint; int tcl_rv; if (outbuf == NULL) { return(0); } str = Tcl_GetString(data); if (!str) { return(0); } tmpbuf[0] = '0'; tmpbuf[1] = 'x'; tmpbuf[4] = '\0'; for (str = Tcl_GetString(data); *str; str++) { tmpbuf[2] = *str; str++; if (!*str) { break; } tmpbuf[3] = *str; tcl_rv = Tcl_GetInt(NULL, tmpbuf, &tmpint); if (tcl_rv != TCL_OK) { return(0); } outbuf[outbufidx] = tmpint; outbufidx++; if (outbufidx >= outbuflen) { break; } } return(outbufidx); } /* PKCS#11 Mutex functions implementation that use Tcl Mutexes */ MODULE_SCOPE CK_RV tclpkcs11_create_mutex(void **mutex) { Tcl_Mutex *retval; if (!mutex) { return(CKR_GENERAL_ERROR); } retval = (Tcl_Mutex *) ckalloc(sizeof(*retval)); memset(retval, 0, sizeof(*retval)); *mutex = retval; return(CKR_OK); } MODULE_SCOPE CK_RV tclpkcs11_lock_mutex(void *mutex) { if (!mutex) { return(CKR_GENERAL_ERROR); } Tcl_MutexLock(*mutex); return(CKR_OK); } MODULE_SCOPE CK_RV tclpkcs11_unlock_mutex(void *mutex) { if (!mutex) { return(CKR_GENERAL_ERROR); } Tcl_MutexUnlock(*mutex); return(CKR_OK); } MODULE_SCOPE CK_RV tclpkcs11_destroy_mutex(void *mutex) { if (!mutex) { return(CKR_GENERAL_ERROR); } Tcl_MutexFinalize(*mutex); ckfree(mutex); return(CKR_OK); } /* Convience function to start a session if one is not already active */ MODULE_SCOPE int tclpkcs11_start_session(struct tclpkcs11_handle *handle, CK_SLOT_ID slot) { CK_SESSION_HANDLE tmp_session; CK_RV chk_rv; if (handle->session != -1) { if (handle->session_slot == slot) { return(CKR_OK); } /* Close the existing session and create a new one */ chk_rv = handle->pkcs11->C_CloseSession(handle->session); handle->session = -1; handle->session_slot = -1; if (chk_rv != CKR_OK) { return(chk_rv); } } chk_rv = handle->pkcs11->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, &tmp_session); if (chk_rv != CKR_OK) { handle->pkcs11->C_CloseSession(handle->session); handle->session = -1; handle->session_slot = -1; return(chk_rv); } handle->session = tmp_session; handle->session_slot = slot; return(CKR_OK); } MODULE_SCOPE int tclpkcs11_close_session(struct tclpkcs11_handle *handle) { CK_RV chk_rv; if (handle->session != -1) { chk_rv = handle->pkcs11->C_CloseSession(handle->session); handle->session = -1; handle->session_slot = -1; if (chk_rv != CKR_OK) { return(chk_rv); } } return(CKR_OK); } /* * Platform Specific Functions */ MODULE_SCOPE void *tclpkcs11_int_load_module(const char *pathname) { #if defined(TCL_INCLUDES_LOADFILE) int tcl_rv; Tcl_LoadHandle *new_handle; new_handle = (Tcl_LoadHandle *) ckalloc(sizeof(*new_handle)); tcl_rv = Tcl_LoadFile(NULL, Tcl_NewStringObj(pathname, -1), NULL, 0, NULL, new_handle); if (tcl_rv != TCL_OK) { return(NULL); } return(new_handle); #elif defined(HAVE_DLOPEN) return(dlopen(pathname, RTLD_NOW | RTLD_GLOBAL)); #elif defined(HAVE_SHL_LOAD) return(shl_load(pathname, BIND_DEFERRED, 0L)); #elif defined(_WIN32) return(LoadLibrary(pathname)); #endif return(NULL); } MODULE_SCOPE void tclpkcs11_int_unload_module(void *handle) { #if defined(TCL_INCLUDES_LOADFILE) Tcl_LoadHandle *tcl_handle; tcl_handle = handle; Tcl_FSUnloadFile(NULL, *tcl_handle); ckfree(handle); #elif defined(HAVE_DLOPEN) dlclose(handle); #elif defined(HAVE_SHL_LOAD) shl_unload(handle); #elif defined(_WIN32) FreeLibrary(handle); #endif return; } MODULE_SCOPE void *tclpkcs11_int_lookup_sym(void *handle, const char *sym) { #if defined(TCL_INCLUDES_LOADFILE) Tcl_LoadHandle *tcl_handle; void *retval; tcl_handle = handle; retval = Tcl_FindSymbol(NULL, *tcl_handle, sym); return(retval); #elif defined(HAVE_DLOPEN) return(dlsym(handle, sym)); #elif defined(HAVE_SHL_LOAD) void *retval; int shl_findsym_ret; shl_findsym_ret = shl_findsym(handle, sym, TYPE_PROCEDURE, &retval); if (shl_findsym_ret != 0) { return(NULL); } return(retval); #elif defined(_WIN32) return(GetProcAddress(handle, sym)); #endif return(NULL); } /* * Tcl Commands */ MODULE_SCOPE int tclpkcs11_load_module(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *new_handle; const char *pathname; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle; void *handle; int is_new_entry; CK_C_INITIALIZE_ARGS initargs; CK_C_GetFunctionList getFuncList; CK_FUNCTION_LIST_PTR pkcs11_function_list = NULL; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata\n", -1)); return(TCL_ERROR); } if (objc != 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::loadmodule filename\"", -1)); return(TCL_ERROR); } pathname = Tcl_GetString(objv[1]); if (!pathname) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid pathname", -1)); return(TCL_ERROR); } handle = tclpkcs11_int_load_module(pathname); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to load", -1)); return(TCL_ERROR); } getFuncList = tclpkcs11_int_lookup_sym(handle, "C_GetFunctionList"); if (!getFuncList) { Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to locate C_GetFunctionList symbol in PKCS#11 module", -1)); return(TCL_ERROR); } chk_rv = getFuncList(&pkcs11_function_list); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } if (!pkcs11_function_list) { Tcl_SetObjResult(interp, Tcl_NewStringObj("C_GetFunctionList returned invalid data", -1)); return(TCL_ERROR); } if (!pkcs11_function_list->C_Initialize) { Tcl_SetObjResult(interp, Tcl_NewStringObj("C_GetFunctionList returned incomplete data", -1)); return(TCL_ERROR); } initargs.CreateMutex = tclpkcs11_create_mutex; initargs.DestroyMutex = tclpkcs11_destroy_mutex; initargs.LockMutex = tclpkcs11_lock_mutex; initargs.UnlockMutex = tclpkcs11_unlock_mutex; initargs.flags = 0; initargs.LibraryFlags = NULL; initargs.pReserved = NULL; chk_rv = pkcs11_function_list->C_Initialize(&initargs); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle = Tcl_NewStringObj("pkcsmod", -1); Tcl_AppendObjToObj(tcl_handle, Tcl_NewLongObj(interpdata->handles_idx)); (interpdata->handles_idx)++; tcl_handle_entry = Tcl_CreateHashEntry(&interpdata->handles, (const char *) tcl_handle, &is_new_entry); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create new hash entry", -1)); return(TCL_ERROR); } /* Allocate the per-handle structure */ new_handle = (struct tclpkcs11_handle *) ckalloc(sizeof(*new_handle)); /* Initialize the per-handle structure */ new_handle->base = handle; new_handle->pkcs11 = pkcs11_function_list; new_handle->session = -1; new_handle->session_slot = -1; Tcl_SetHashValue(tcl_handle_entry, (ClientData) new_handle); Tcl_SetObjResult(interp, tcl_handle); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_unload_module(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::unloadmodule handle\"", -1)); return(TCL_ERROR); } tcl_handle = objv[1]; interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } /* Log out of the PKCS11 module */ handle->pkcs11->C_Logout(handle->session); /* Close the session, cleaning up all the session objects */ tclpkcs11_close_session(handle); /* Ask the PKCS#11 Provider to terminate */ chk_rv = handle->pkcs11->C_Finalize(NULL); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } /* Delete our hash entry */ Tcl_DeleteHashEntry(tcl_handle_entry); /* Attempt to unload the module */ tclpkcs11_int_unload_module(handle->base); /* Free our allocated handle */ ckfree((char *) handle); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_list_slots(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle; Tcl_Obj *ret_list, *curr_item_list, *flags_list, *slot_desc; CK_SLOT_ID_PTR slots; CK_SLOT_INFO slotInfo; CK_TOKEN_INFO tokenInfo; CK_ULONG numSlots, currSlot; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::listslots handle\"", -1)); return(TCL_ERROR); } tcl_handle = objv[1]; interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } chk_rv = handle->pkcs11->C_GetSlotList(FALSE, NULL, &numSlots); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } slots = (CK_SLOT_ID_PTR) ckalloc(sizeof(*slots) * numSlots); chk_rv = handle->pkcs11->C_GetSlotList(FALSE, slots, &numSlots); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } ret_list = Tcl_NewObj(); for (currSlot = 0; currSlot < numSlots; currSlot++) { chk_rv = handle->pkcs11->C_GetSlotInfo(slots[currSlot], &slotInfo); curr_item_list = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewLongObj(slots[currSlot])); flags_list = Tcl_NewObj(); if (chk_rv != CKR_OK) { /* Add an empty string as the token label */ Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("", 0)); /* Add the list of existing flags (none) */ Tcl_ListObjAppendElement(interp, curr_item_list, flags_list); /* Add this item to the list */ Tcl_ListObjAppendElement(interp, ret_list, curr_item_list); continue; } slot_desc = NULL; if ((slotInfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("TOKEN_PRESENT", -1)); chk_rv = handle->pkcs11->C_GetTokenInfo(slots[currSlot], &tokenInfo); if (chk_rv == CKR_OK) { /* Add the token label as the slot label */ if (!slot_desc) { slot_desc = Tcl_NewStringObj((const char *) tokenInfo.label, 32); } if ((tokenInfo.flags & CKF_RNG) == CKF_RNG) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("RNG", -1)); } if ((tokenInfo.flags & CKF_WRITE_PROTECTED) == CKF_WRITE_PROTECTED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("WRITE_PROTECTED", -1)); } if ((tokenInfo.flags & CKF_LOGIN_REQUIRED) == CKF_LOGIN_REQUIRED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("LOGIN_REQUIRED", -1)); } if ((tokenInfo.flags & CKF_USER_PIN_INITIALIZED) == CKF_USER_PIN_INITIALIZED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("USER_PIN_INITIALIZED", -1)); } if ((tokenInfo.flags & CKF_RESTORE_KEY_NOT_NEEDED) == CKF_RESTORE_KEY_NOT_NEEDED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("RESTORE_KEY_NOT_NEEDED", -1)); } if ((tokenInfo.flags & CKF_CLOCK_ON_TOKEN) == CKF_CLOCK_ON_TOKEN) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("CLOCK_ON_TOKEN", -1)); } if ((tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == CKF_PROTECTED_AUTHENTICATION_PATH) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("PROTECTED_AUTHENTICATION_PATH", -1)); } if ((tokenInfo.flags & CKF_DUAL_CRYPTO_OPERATIONS) == CKF_DUAL_CRYPTO_OPERATIONS) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("DUAL_CRYPTO_OPERATIONS", -1)); } if ((tokenInfo.flags & CKF_TOKEN_INITIALIZED) == CKF_TOKEN_INITIALIZED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("TOKEN_INITIALIZED", -1)); } if ((tokenInfo.flags & CKF_SECONDARY_AUTHENTICATION) == CKF_SECONDARY_AUTHENTICATION) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("SECONDARY_AUTHENTICATION", -1)); } if ((tokenInfo.flags & CKF_USER_PIN_COUNT_LOW) == CKF_USER_PIN_COUNT_LOW) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("USER_PIN_COUNT_LOW", -1)); } if ((tokenInfo.flags & CKF_USER_PIN_FINAL_TRY) == CKF_USER_PIN_FINAL_TRY) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("USER_PIN_FINAL_TRY", -1)); } if ((tokenInfo.flags & CKF_USER_PIN_LOCKED) == CKF_USER_PIN_LOCKED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("USER_PIN_LOCKED", -1)); } if ((tokenInfo.flags & CKF_USER_PIN_TO_BE_CHANGED) == CKF_USER_PIN_TO_BE_CHANGED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("USER_PIN_TO_BE_CHANGED", -1)); } if ((tokenInfo.flags & CKF_SO_PIN_COUNT_LOW) == CKF_SO_PIN_COUNT_LOW) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("SO_PIN_COUNT_LOW", -1)); } if ((tokenInfo.flags & CKF_SO_PIN_FINAL_TRY) == CKF_SO_PIN_FINAL_TRY) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("SO_PIN_FINAL_TRY", -1)); } if ((tokenInfo.flags & CKF_SO_PIN_LOCKED) == CKF_SO_PIN_LOCKED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("SO_PIN_LOCKED", -1)); } if ((tokenInfo.flags & CKF_SO_PIN_TO_BE_CHANGED) == CKF_SO_PIN_TO_BE_CHANGED) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("SO_PIN_TO_BE_CHANGED", -1)); } } } /* Add additional slot flags */ if ((slotInfo.flags & CKF_REMOVABLE_DEVICE) == CKF_REMOVABLE_DEVICE) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("REMOVABLE_DEVICE", -1)); } if ((slotInfo.flags & CKF_HW_SLOT) == CKF_HW_SLOT) { Tcl_ListObjAppendElement(interp, flags_list, Tcl_NewStringObj("HW_SLOT", -1)); } if (slot_desc) { /* If we found a more descriptive slot description, use it */ Tcl_ListObjAppendElement(interp, curr_item_list, slot_desc); } else { /* Add the slot description as the label for tokens with nothing in them */ Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj((const char *) slotInfo.slotDescription, 32)); } Tcl_ListObjAppendElement(interp, curr_item_list, flags_list); Tcl_ListObjAppendElement(interp, ret_list, curr_item_list); } Tcl_SetObjResult(interp, ret_list); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_list_certs(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle, *tcl_slotid; long slotid_long; Tcl_Obj *obj_label, *obj_cert, *obj_id; Tcl_Obj *ret_list, *curr_item_list; Tcl_Obj *parse_cert_cmd; int tcl_rv; CK_SLOT_ID slotid; CK_OBJECT_HANDLE hObject; CK_ULONG ulObjectCount; CK_ATTRIBUTE template[] = { {CKA_CLASS, NULL, 0}, {CKA_LABEL, NULL, 0}, {CKA_ID, NULL, 0}, {CKA_VALUE, NULL, 0} }, *curr_attr; CK_ULONG curr_attr_idx; CK_OBJECT_CLASS *objectclass; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 3) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::listcerts handle slot\"", -1)); return(TCL_ERROR); } tcl_handle = objv[1]; tcl_slotid = objv[2]; interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } tcl_rv = Tcl_GetLongFromObj(interp, tcl_slotid, &slotid_long); if (tcl_rv != TCL_OK) { return(tcl_rv); } slotid = slotid_long; chk_rv = tclpkcs11_start_session(handle, slotid); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } chk_rv = handle->pkcs11->C_FindObjectsInit(handle->session, NULL, 0); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } ret_list = Tcl_NewObj(); while (1) { chk_rv = handle->pkcs11->C_FindObjects(handle->session, &hObject, 1, &ulObjectCount); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); handle->pkcs11->C_FindObjectsFinal(handle->session); return(TCL_ERROR); } if (ulObjectCount == 0) { break; } if (ulObjectCount != 1) { Tcl_SetObjResult(interp, Tcl_NewStringObj("FindObjects() returned a weird number of objects.", -1)); handle->pkcs11->C_FindObjectsFinal(handle->session); return(TCL_ERROR); } for (curr_attr_idx = 0; curr_attr_idx < (sizeof(template) / sizeof(template[0])); curr_attr_idx++) { curr_attr = &template[curr_attr_idx]; if (curr_attr->pValue) { ckfree(curr_attr->pValue); } curr_attr->pValue = NULL; curr_attr->ulValueLen = 0; } /* Determine size of values to allocate */ chk_rv = handle->pkcs11->C_GetAttributeValue(handle->session, hObject, template, sizeof(template) / sizeof(template[0])); if (chk_rv == CKR_ATTRIBUTE_TYPE_INVALID || chk_rv == CKR_ATTRIBUTE_SENSITIVE || chk_rv == CKR_BUFFER_TOO_SMALL) { chk_rv = CKR_OK; } if (chk_rv != CKR_OK) { /* Skip this object if we are not able to process it */ continue; } /* Allocate values */ for (curr_attr_idx = 0; curr_attr_idx < (sizeof(template) / sizeof(template[0])); curr_attr_idx++) { curr_attr = &template[curr_attr_idx]; if (((CK_LONG) curr_attr->ulValueLen) != ((CK_LONG) -1)) { curr_attr->pValue = (void *) ckalloc(curr_attr->ulValueLen); } } /* Populate template values */ chk_rv = handle->pkcs11->C_GetAttributeValue(handle->session, hObject, template, sizeof(template) / sizeof(template[0])); if (chk_rv != CKR_OK && chk_rv != CKR_ATTRIBUTE_SENSITIVE && chk_rv != CKR_ATTRIBUTE_TYPE_INVALID && chk_rv != CKR_BUFFER_TOO_SMALL) { /* Return an error if we are unable to process this entry due to unexpected errors */ for (curr_attr_idx = 0; curr_attr_idx < (sizeof(template) / sizeof(template[0])); curr_attr_idx++) { curr_attr = &template[curr_attr_idx]; if (curr_attr->pValue) { ckfree(curr_attr->pValue); } } Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); handle->pkcs11->C_FindObjectsFinal(handle->session); return(TCL_ERROR); } /* Extract certificate data */ obj_label = NULL; obj_id = NULL; obj_cert = NULL; objectclass = NULL; for (curr_attr_idx = 0; curr_attr_idx < (sizeof(template) / sizeof(template[0])); curr_attr_idx++) { curr_attr = &template[curr_attr_idx]; if (!curr_attr->pValue) { continue; } switch (curr_attr->type) { case CKA_CLASS: objectclass = (CK_OBJECT_CLASS *) curr_attr->pValue; if (*objectclass != CKO_CERTIFICATE) { continue; } break; case CKA_LABEL: obj_label = Tcl_NewStringObj(curr_attr->pValue, curr_attr->ulValueLen); break; case CKA_ID: /* Convert the ID into a readable string */ obj_id = tclpkcs11_bytearray_to_string(curr_attr->pValue, curr_attr->ulValueLen); break; case CKA_VALUE: if (!objectclass) { break; } obj_cert = Tcl_NewByteArrayObj(curr_attr->pValue, curr_attr->ulValueLen); break; } ckfree(curr_attr->pValue); curr_attr->pValue = NULL; } /* Add this certificate data to return list, if all found */ if (obj_label == NULL || obj_id == NULL || obj_cert == NULL) { continue; } /* Create the current item list */ curr_item_list = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("pkcs11_handle", -1)); Tcl_ListObjAppendElement(interp, curr_item_list, tcl_handle); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("pkcs11_slotid", -1)); Tcl_ListObjAppendElement(interp, curr_item_list, tcl_slotid); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("pkcs11_id", -1)); Tcl_ListObjAppendElement(interp, curr_item_list, obj_id); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("pkcs11_label", -1)); Tcl_ListObjAppendElement(interp, curr_item_list, obj_label); /* Call "::pki::x509::parse_cert" to parse the cert */ parse_cert_cmd = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, parse_cert_cmd, Tcl_NewStringObj("::pki::x509::parse_cert", -1)); Tcl_ListObjAppendElement(interp, parse_cert_cmd, obj_cert); tcl_rv = Tcl_EvalObjEx(interp, parse_cert_cmd, 0); if (tcl_rv != TCL_OK) { continue; } /* Add results of [parse_cert] to our return value */ Tcl_ListObjAppendList(interp, curr_item_list, Tcl_GetObjResult(interp)); /* * Override the "type" so that [array set] returns our new * type, but we can still parse through the list and figure * out the real subordinate type */ Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("type", -1)); Tcl_ListObjAppendElement(interp, curr_item_list, Tcl_NewStringObj("pkcs11", -1)); /* Add the current item to the return value list */ Tcl_ListObjAppendElement(interp, ret_list, curr_item_list); } /* Terminate search */ handle->pkcs11->C_FindObjectsFinal(handle->session); /* Return */ Tcl_SetObjResult(interp, ret_list); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_login(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle, *tcl_slotid, *tcl_password; long slotid_long; char *password; int password_len; int tcl_rv; CK_SLOT_ID slotid; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 4) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::login handle slot password\"", -1)); return(TCL_ERROR); } tcl_handle = objv[1]; tcl_slotid = objv[2]; tcl_password = objv[3]; interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } tcl_rv = Tcl_GetLongFromObj(interp, tcl_slotid, &slotid_long); if (tcl_rv != TCL_OK) { return(tcl_rv); } slotid = slotid_long; chk_rv = tclpkcs11_start_session(handle, slotid); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } password = Tcl_GetStringFromObj(tcl_password, &password_len); chk_rv = handle->pkcs11->C_Login(handle->session, CKU_USER, (CK_UTF8CHAR_PTR) password, password_len); switch (chk_rv) { case CKR_OK: case CKR_USER_ALREADY_LOGGED_IN: Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); break; case CKR_PIN_INCORRECT: Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); break; default: Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } return(TCL_OK); } MODULE_SCOPE int tclpkcs11_logout(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *tcl_handle, *tcl_slotid; long slotid_long; int tcl_rv; CK_SLOT_ID slotid; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 3) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::login handle slot\"", -1)); return(TCL_ERROR); } tcl_handle = objv[1]; tcl_slotid = objv[2]; interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } tcl_rv = Tcl_GetLongFromObj(interp, tcl_slotid, &slotid_long); if (tcl_rv != TCL_OK) { return(tcl_rv); } slotid = slotid_long; chk_rv = tclpkcs11_start_session(handle, slotid); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } chk_rv = handle->pkcs11->C_Logout(handle->session); if (chk_rv != CKR_OK) { if (chk_rv == CKR_DEVICE_REMOVED) { handle->pkcs11->C_CloseSession(handle->session); handle->session = -1; handle->session_slot = -1; } else { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_perform_pki(int encrypt, ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; unsigned char *input, resultbuf[1024]; unsigned long tcl_strtobytearray_rv; Tcl_HashEntry *tcl_handle_entry; Tcl_Obj *pki_real_cmd; Tcl_Obj *tcl_keylist, **tcl_keylist_values, *tcl_keylist_key, *tcl_keylist_val; Tcl_Obj *tcl_mode, *tcl_input; Tcl_Obj *tcl_handle = NULL, *tcl_slotid = NULL, *tcl_objid = NULL; Tcl_Obj *tcl_result; long slotid_long; int tcl_keylist_llength, idx; int input_len; CK_ULONG resultbuf_len; int sign; int tcl_rv; CK_SLOT_ID slotid; CK_OBJECT_HANDLE hObject; CK_ULONG foundObjs; CK_OBJECT_CLASS objectclass_pk; CK_ATTRIBUTE template[] = { {CKA_ID, NULL, 0}, {CKA_CLASS, NULL, 0}, }; CK_MECHANISM mechanism = {CKM_RSA_PKCS, NULL, 0}; CK_RV chk_rv; if (!cd) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid clientdata", -1)); return(TCL_ERROR); } if (objc != 4) { if (encrypt) { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::encrypt mode input keylist\"", -1)); } else { Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"pki::pkcs11::decrypt mode input keylist\"", -1)); } return(TCL_ERROR); } tcl_mode = objv[1]; tcl_input = objv[2]; tcl_keylist = objv[3]; /* * Parse the "keylist" argument and remove the extraneous "type * pkcs11" entry so we can pass it to around as needed * * Also, while we are here, pick out the elements we can */ /* Duplicate the object so we can modify it */ if (Tcl_IsShared(tcl_keylist)) { tcl_keylist = Tcl_DuplicateObj(tcl_keylist); } tcl_rv = Tcl_ListObjGetElements(interp, tcl_keylist, &tcl_keylist_llength, &tcl_keylist_values); if (tcl_rv != TCL_OK) { return(tcl_rv); } if ((tcl_keylist_llength % 2) != 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj("list must have an even number of elements", -1)); return(TCL_ERROR); } for (idx = 0; idx < tcl_keylist_llength; idx += 2) { tcl_keylist_key = tcl_keylist_values[idx]; tcl_keylist_val = tcl_keylist_values[idx + 1]; if (strcmp(Tcl_GetString(tcl_keylist_key), "type") == 0) { if (strcmp(Tcl_GetString(tcl_keylist_val), "pkcs11") == 0) { /* Remove "type pkcs11" from list */ tcl_rv = Tcl_ListObjReplace(interp, tcl_keylist, idx, 2, 0, NULL); } continue; } if (strcmp(Tcl_GetString(tcl_keylist_key), "pkcs11_handle") == 0) { tcl_handle = tcl_keylist_val; continue; } if (strcmp(Tcl_GetString(tcl_keylist_key), "pkcs11_slotid") == 0) { tcl_slotid = tcl_keylist_val; continue; } if (strcmp(Tcl_GetString(tcl_keylist_key), "pkcs11_id") == 0) { tcl_objid = tcl_keylist_val; continue; } } if (strcmp(Tcl_GetString(tcl_mode), "pub") == 0) { /* Public Key Operations can be performed by the Tcl PKI Module */ pki_real_cmd = Tcl_NewObj(); if (encrypt) { Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("::pki::encrypt", -1)); Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("-nopad", -1)); } else { Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("::pki::decrypt", -1)); Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("-nounpad", -1)); } Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("-pub", -1)); Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("-binary", -1)); Tcl_ListObjAppendElement(interp, pki_real_cmd, Tcl_NewStringObj("--", -1)); Tcl_ListObjAppendElement(interp, pki_real_cmd, tcl_input); Tcl_ListObjAppendElement(interp, pki_real_cmd, tcl_keylist); return(Tcl_EvalObjEx(interp, pki_real_cmd, 0)); } if (!tcl_handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("could not find element named \"pkcs11_handle\" in keylist", -1)); return(TCL_ERROR); } if (!tcl_slotid) { Tcl_SetObjResult(interp, Tcl_NewStringObj("could not find element named \"pkcs11_slotid\" in keylist", -1)); return(TCL_ERROR); } if (!tcl_objid) { Tcl_SetObjResult(interp, Tcl_NewStringObj("could not find element named \"pkcs11_id\" in keylist", -1)); return(TCL_ERROR); } interpdata = (struct tclpkcs11_interpdata *) cd; tcl_handle_entry = Tcl_FindHashEntry(&interpdata->handles, (const char *) tcl_handle); if (!tcl_handle_entry) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (!handle) { Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid handle", -1)); return(TCL_ERROR); } /* * Find the PKCS#11 object ID that cooresponds to this certificate's * private key */ tcl_rv = Tcl_GetLongFromObj(interp, tcl_slotid, &slotid_long); if (tcl_rv != TCL_OK) { return(tcl_rv); } slotid = slotid_long; chk_rv = tclpkcs11_start_session(handle, slotid); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } /* CKA_ID */ template[0].pValue = ckalloc(Tcl_GetCharLength(tcl_objid) / 2); tcl_strtobytearray_rv = tclpkcs11_string_to_bytearray(tcl_objid, template[0].pValue, Tcl_GetCharLength(tcl_objid) / 2); template[0].ulValueLen = tcl_strtobytearray_rv; /* CKA_CLASS */ objectclass_pk = CKO_PRIVATE_KEY; template[1].pValue = &objectclass_pk; template[1].ulValueLen = sizeof(objectclass_pk); chk_rv = handle->pkcs11->C_FindObjectsInit(handle->session, template, sizeof(template) / sizeof(template[0])); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } chk_rv = handle->pkcs11->C_FindObjects(handle->session, &hObject, 1, &foundObjs); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); handle->pkcs11->C_FindObjectsFinal(handle->session); return(TCL_ERROR); } /* Terminate Search */ handle->pkcs11->C_FindObjectsFinal(handle->session); if (foundObjs < 1) { Tcl_SetObjResult(interp, Tcl_NewStringObj("PKCS11_ERROR MAYBE_LOGIN", -1)); return(TCL_ERROR); } /* Perform the PKI operation (encrypt/decrypt) */ input = Tcl_GetByteArrayFromObj(tcl_input, &input_len); if (encrypt) { sign = 0; chk_rv = handle->pkcs11->C_EncryptInit(handle->session, &mechanism, hObject); if (chk_rv != CKR_OK) { if (chk_rv == CKR_FUNCTION_NOT_SUPPORTED) { sign = 1; chk_rv = handle->pkcs11->C_SignInit(handle->session, &mechanism, hObject); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } } } resultbuf_len = sizeof(resultbuf); if (!sign) { chk_rv = handle->pkcs11->C_Encrypt(handle->session, input, input_len, resultbuf, &resultbuf_len); } else { /* Some PKCS#11 drivers will not accept pre-padded input, so we must unpad it here */ if (input_len > 3) { if (input[0] == 0x00 && input[1] == 0x01) { input = input + 2; input_len -= 2; while (*input == 0xff && input_len > 0) { input++; input_len--; } if (input_len) { if (input[0] == 0x00) { input++; input_len--; } } } } chk_rv = handle->pkcs11->C_Sign(handle->session, input, input_len, resultbuf, &resultbuf_len); } if (chk_rv != CKR_OK) { if (chk_rv == CKR_BUFFER_TOO_SMALL) { /* Terminate decryption operation */ handle->pkcs11->C_DecryptFinal(handle->session, NULL, 0); } Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } } else { chk_rv = handle->pkcs11->C_DecryptInit(handle->session, &mechanism, hObject); if (chk_rv != CKR_OK) { Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } resultbuf_len = sizeof(resultbuf); chk_rv = handle->pkcs11->C_Decrypt(handle->session, input, input_len, resultbuf, &resultbuf_len); if (chk_rv != CKR_OK) { if (chk_rv == CKR_BUFFER_TOO_SMALL) { /* Terminate decryption operation */ handle->pkcs11->C_DecryptFinal(handle->session, NULL, 0); } Tcl_SetObjResult(interp, tclpkcs11_pkcs11_error(chk_rv)); return(TCL_ERROR); } } tcl_result = Tcl_NewByteArrayObj(resultbuf, resultbuf_len); Tcl_SetObjResult(interp, tcl_result); return(TCL_OK); } MODULE_SCOPE int tclpkcs11_encrypt(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { return(tclpkcs11_perform_pki(1, cd, interp, objc, objv)); } MODULE_SCOPE int tclpkcs11_decrypt(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { return(tclpkcs11_perform_pki(0, cd, interp, objc, objv)); } MODULE_SCOPE void tclpkcs11_unloadall(ClientData cd) { struct tclpkcs11_interpdata *interpdata; struct tclpkcs11_handle *handle; Tcl_HashEntry *tcl_handle_entry; Tcl_HashSearch search; if (!cd) { return; } interpdata = (struct tclpkcs11_interpdata *) cd; for ( tcl_handle_entry = Tcl_FirstHashEntry(&interpdata->handles, &search); tcl_handle_entry; tcl_handle_entry = Tcl_NextHashEntry(&search) ) { handle = (struct tclpkcs11_handle *) Tcl_GetHashValue(tcl_handle_entry); if (handle->pkcs11 && handle->pkcs11->C_Finalize) { handle->pkcs11->C_Finalize(NULL); } tclpkcs11_int_unload_module(handle->base); ckfree((char *) handle); } return; } /* * Tcl Loadable Module Initialization */ int Tclpkcs11_Init(Tcl_Interp *interp) { struct tclpkcs11_interpdata *interpdata; Tcl_Command tclCreatComm_ret; const char *tclPkgReq_ret; int tclPkgProv_ret; #ifdef USE_TCL_STUBS const char *tclInitStubs_ret; /* Initialize Stubs */ tclInitStubs_ret = Tcl_InitStubs(interp, "8.4", 0); if (!tclInitStubs_ret) { return(TCL_ERROR); } #endif tclPkgReq_ret = Tcl_PkgRequire(interp, "pki", "0.1", 0); if (!tclPkgReq_ret) { return(TCL_ERROR); } interpdata = (struct tclpkcs11_interpdata *) ckalloc(sizeof(*interpdata)); /* Initialize InterpData structure */ Tcl_InitObjHashTable(&interpdata->handles); interpdata->handles_idx = 0; tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::loadmodule", tclpkcs11_load_module, interpdata, NULL); if (!tclCreatComm_ret) { ckfree((char *) interpdata); return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::unloadmodule", tclpkcs11_unload_module, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::listslots", tclpkcs11_list_slots, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::listcerts", tclpkcs11_list_certs, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::login", tclpkcs11_login, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::logout", tclpkcs11_logout, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::encrypt", tclpkcs11_encrypt, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } tclCreatComm_ret = Tcl_CreateObjCommand(interp, "pki::pkcs11::decrypt", tclpkcs11_decrypt, interpdata, NULL); if (!tclCreatComm_ret) { return(TCL_ERROR); } /* Create an exit handler to unload and close all PKCS#11 modules */ Tcl_CreateExitHandler(tclpkcs11_unloadall, interpdata); /* Register PKI handlers */ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("pki::handlers", -1), Tcl_NewStringObj("pkcs11", -1), Tcl_NewStringObj("::pki::pkcs11::encrypt ::pki::pkcs11::decrypt", -1), TCL_GLOBAL_ONLY ); tclPkgProv_ret = Tcl_PkgProvide(interp, "pki::pkcs11", PACKAGE_VERSION); if (tclPkgProv_ret != TCL_OK) { return(tclPkgProv_ret); } return(TCL_OK); }