From dae729705015826f34334c01d3d80d84f769d832 Mon Sep 17 00:00:00 2001 From: Alessio Di Mauro Date: Tue, 14 Jul 2015 14:13:02 +0200 Subject: [PATCH] Major refactor and vendor abstraction. --- lib/ykpiv.c | 28 ++++++---- lib/ykpiv.h | 4 +- lib/ykpiv.map | 3 +- ykcs11/Makefile.am | 3 +- ykcs11/vendors.c | 32 ++++++++---- ykcs11/vendors.h | 31 ++++++++---- ykcs11/ykcs11.c | 124 +++++++++++++++++++++++++++++++-------------- ykcs11/yubico.c | 75 +++++++++++++++++++-------- ykcs11/yubico.h | 15 ++++-- 9 files changed, 212 insertions(+), 103 deletions(-) diff --git a/lib/ykpiv.c b/lib/ykpiv.c index b3a5114..e8182ad 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -127,6 +127,10 @@ ykpiv_rc ykpiv_disconnect(ykpiv_state *state) { } ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { + return ykpiv_connect2(state, wanted, NULL, 0); +} + +ykpiv_rc ykpiv_connect2(ykpiv_state *state, const char *wanted, unsigned char **readers, unsigned long *len) { unsigned long num_readers = 0; unsigned long active_protocol; char reader_buf[1024]; @@ -165,19 +169,21 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { return YKPIV_PCSC_ERROR; } - // TODO: improve here - state->n_readers = 0; - state->tot_readers_len = num_readers; - reader_ptr = reader_buf; - for (i = 0; i < num_readers; i++) - if (reader_buf[i] == '\0' && i != num_readers - 1) { - strcpy(state->readers[state->n_readers], reader_ptr); // TODO: strdup? - state->n_readers = state->n_readers + 1; - reader_ptr += i + 1; + // Save available readers (aka PKCS11 slots) + if (readers != NULL) { + *readers = malloc(sizeof(char) * num_readers); + if (*readers == NULL) { + if(state->verbose) { + fprintf (stderr, "error: malloc failed"); + } + SCardReleaseContext(state->context); + return YKPIV_MEMORY_ERROR; } - // ********* + memcpy(*readers, reader_buf, num_readers); + *len = num_readers; + } - reader_ptr = reader_buf; // TODO: reader_buf is never free'd + reader_ptr = reader_buf; if(wanted) { while(*reader_ptr != '\0') { if(strstr(reader_ptr, wanted)) { diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 319da54..be1fb5b 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -64,6 +64,7 @@ extern "C" ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); ykpiv_rc ykpiv_done(ykpiv_state *state); ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); + ykpiv_rc ykpiv_connect2(ykpiv_state *state, const char *wanted, unsigned char **readers, unsigned long *len); ykpiv_rc ykpiv_disconnect(ykpiv_state *state); ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ, const unsigned char *in_data, long in_len, @@ -85,9 +86,6 @@ extern "C" ykpiv_rc ykpiv_save_object(ykpiv_state *state, int object_id, unsigned char *indata, size_t len); - ykpiv_rc ykpiv_get_reader_slot_number(ykpiv_state *state, unsigned long *slots, unsigned long *total); - ykpiv_rc ykpiv_get_reader_slot(ykpiv_state *state, unsigned long slot, char *reader); - #define YKPIV_ALGO_3DES 0x03 #define YKPIV_ALGO_RSA1024 0x06 #define YKPIV_ALGO_RSA2048 0x07 diff --git a/lib/ykpiv.map b/lib/ykpiv.map index d7fb74c..09c7847 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -52,6 +52,5 @@ YKPIV_0.2.0 { global: ykpiv_decipher_data; - ykpiv_get_reader_slot_number; - ykpiv_get_reader_slot; + ykpiv_connect2; } YKPIV_0.1.0; diff --git a/ykcs11/Makefile.am b/ykcs11/Makefile.am index 050c4dc..22fbfea 100644 --- a/ykcs11/Makefile.am +++ b/ykcs11/Makefile.am @@ -34,7 +34,8 @@ AM_CPPFLAGS += -I$(top_srcdir)/lib -I$(top_builddir)/lib lib_LTLIBRARIES = libykcs11.la libykcs11_la_SOURCES = ykcs11.c version.c ykcs11.pc.in ykcs11.map -libykcs11_la_SOURCES += vendors.c vendor.h yubico.c yubico. h +libykcs11_la_SOURCES += vendors.c vendor.h yubico.c yubico.h +libykcs11_la_SOURCES += utils.h utils.c #internal.h #libykcs11_la_SOURCES += error.c diff --git a/ykcs11/vendors.c b/ykcs11/vendors.c index 179cc99..0353368 100644 --- a/ykcs11/vendors.c +++ b/ykcs11/vendors.c @@ -16,20 +16,30 @@ vendor_t get_vendor(vendor_id_t vid) { switch (vid) { case YUBICO: - v.get_version = YUBICO_get_version; - v.get_label = YUBICO_get_label; - v.get_manufacturer = YUBICO_get_manufacturer; - v.get_model = YUBICO_get_model; - v.get_flags = YUBICO_get_flags; + v.get_slot_description = YUBICO_get_slot_description; + v.get_slot_manufacturer = YUBICO_get_slot_manufacturer; + v.get_slot_flags = YUBICO_get_slot_flags; + v.get_slot_version = YUBICO_get_slot_version; + v.get_token_label = YUBICO_get_token_label; + v.get_token_manufacturer = YUBICO_get_token_manufacturer; + v.get_token_model = YUBICO_get_token_model; + v.get_token_flags = YUBICO_get_token_flags; + v.get_token_version = YUBICO_get_token_version; + v.get_token_serial = YUBICO_get_token_serial; break; case UNKNOWN: - v.get_version = NULL; // TODO: make up dummy functions? - v.get_label = NULL; - v.get_manufacturer = NULL; - v.get_model = NULL; - v.get_flags = NULL; - + default: + v.get_slot_description = NULL; + v.get_slot_manufacturer = NULL; + v.get_slot_flags = NULL; + v.get_slot_version = NULL; + v.get_token_label = NULL; + v.get_token_manufacturer = NULL; + v.get_token_model = NULL; + v.get_token_flags = NULL; + v.get_token_version = NULL; + v.get_token_serial = NULL; } return v; diff --git a/ykcs11/vendors.h b/ykcs11/vendors.h index 5f1e867..d82f6c4 100644 --- a/ykcs11/vendors.h +++ b/ykcs11/vendors.h @@ -8,18 +8,29 @@ typedef enum { YUBICO = 0x01 } vendor_id_t; -typedef CK_VERSION (*get_version_f)(CK_UTF8CHAR_PTR, CK_ULONG); -typedef CK_UTF8CHAR_PTR (*get_label_f)(void); -typedef CK_UTF8CHAR_PTR (*get_manufacturer_f)(void); -typedef CK_UTF8CHAR_PTR (*get_model_f)(void); -typedef CK_FLAGS (*get_flags_f)(void); +typedef CK_UTF8CHAR_PTR (*get_s_description_f)(void); +typedef CK_UTF8CHAR_PTR (*get_s_manufacturer_f)(void); +typedef CK_FLAGS (*get_s_flags_f)(void); +typedef CK_VERSION (*get_s_version_f)(CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_UTF8CHAR_PTR (*get_t_label_f)(void); +typedef CK_UTF8CHAR_PTR (*get_t_manufacturer_f)(void); +typedef CK_UTF8CHAR_PTR (*get_t_model_f)(void); +typedef CK_FLAGS (*get_t_flags_f)(void); +typedef CK_VERSION (*get_t_version_f)(CK_UTF8CHAR_PTR, CK_ULONG); +typedef CK_CHAR_PTR (*get_t_serial_f)(void); + typedef struct { - get_version_f get_version; - get_label_f get_label; - get_manufacturer_f get_manufacturer; - get_model_f get_model; - get_flags_f get_flags; + get_s_description_f get_slot_description; + get_s_manufacturer_f get_slot_manufacturer; + get_s_flags_f get_slot_flags; + get_s_version_f get_slot_version; + get_t_label_f get_token_label; + get_t_manufacturer_f get_token_manufacturer; + get_t_model_f get_token_model; + get_t_flags_f get_token_flags; + get_t_version_f get_token_version; + get_t_serial_f get_token_serial; } vendor_t; vendor_id_t get_vendor_id(char *vendor_name); diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index 3165af7..b0aed2c 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -3,6 +3,9 @@ #include #include #include "vendors.h" +#include "utils.h" + +#define HAS_TOKEN(x) ((slots[(x)].info.flags) & (CKF_TOKEN_PRESENT)) #define D(x) do { \ printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \ @@ -10,8 +13,8 @@ printf ("\n"); \ } while (0) -#define YKCS11_DBG 1 // General debug, must be either 1 or 0 -#define YKCS11_DINOUT 1 // Function in/out debug, must be either 1 or 0 +#define YKCS11_DBG 0 // General debug, must be either 1 or 0 +#define YKCS11_DINOUT 0 // Function in/out debug, must be either 1 or 0 #define YKCS11_MANUFACTURER "Yubico (www.yubico.com)" #define YKCS11_LIBDESC "PKCS#11 PIV Library (SP-800-73)" @@ -19,6 +22,8 @@ #define PIV_MIN_PIN_LEN 6 #define PIV_MAX_PIN_LEN 8 +#define YKCS11_MAX_SLOTS 16 + #if YKCS11_DBG #define DBG(x) D(x); #else @@ -35,6 +40,9 @@ static ykpiv_state *piv_state = NULL; +static ykcs11_slot_t slots[YKCS11_MAX_SLOTS]; +static CK_ULONG n_slots = 0; + extern CK_FUNCTION_LIST function_list; /* General Purpose */ @@ -44,7 +52,11 @@ CK_DEFINE_FUNCTION(CK_RV, C_Initialize)( ) { DIN; + CK_CHAR_PTR readers; + CK_ULONG len; + // TODO: check for locks and mutexes + if (piv_state != NULL) return CKR_CRYPTOKI_ALREADY_INITIALIZED; @@ -53,11 +65,14 @@ CK_DEFINE_FUNCTION(CK_RV, C_Initialize)( return CKR_FUNCTION_FAILED; // TODO: better error? } - if(ykpiv_connect(piv_state, NULL) != YKPIV_OK) { + if(ykpiv_connect2(piv_state, NULL, &readers, &len) != YKPIV_OK) { DBG(("Unable to connect to reader")); return CKR_FUNCTION_FAILED; } + parse_readers(readers, len, slots, &n_slots); + DBG(("FOUND: %lu slots %lu len", n_slots, len)); + DOUT; return CKR_OK; } @@ -73,7 +88,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_Finalize)( } if (piv_state == NULL) { - DBG(("Ykpiv is not initialized or already finalized")); + DBG(("libykpiv is not initialized or already finalized")); return CKR_CRYPTOKI_NOT_INITIALIZED; } @@ -89,7 +104,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)( ) { DIN; - CK_VERSION ver = {0.0}; + CK_VERSION ver = {0, 0}; // TODO: set version number pInfo->cryptokiVersion = function_list.version; memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID)); @@ -112,10 +127,12 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionList)( { DIN; if(ppFunctionList == NULL_PTR) { + DBG(("GetFunctionList called with ppFunctionList = NULL")); return CKR_ARGUMENTS_BAD; } *ppFunctionList = &function_list; + DOUT; return CKR_OK; } @@ -128,31 +145,29 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)( ) { DIN; - unsigned long tot_readers_len; - unsigned long n_readers; int i; // TODO: what about tokenPresent? - ykpiv_get_reader_slot_number(piv_state, &n_readers, &tot_readers_len); // TODO: maybe refactor this with a reader struct? + //ykpiv_get_reader_slot_number(piv_state, &n_readers, &tot_reader_len); // TODO: maybe refactor this with a reader struct? if (pSlotList == NULL_PTR) { // Just return the number of slots - *pulCount = n_readers; + *pulCount = n_slots; DOUT; return CKR_OK; } - if (*pulCount < n_readers) { - DBG(("Buffer too small: needed %u, provided %u", n_readers, *pulCount)); + if (*pulCount < n_slots) { + DBG(("Buffer too small: needed %lu, provided %lu", n_slots, *pulCount)); return CKR_BUFFER_TOO_SMALL; } - for (i = 0; i < n_readers; i++) { + for (i = 0; i < n_slots; i++) { pSlotList[i] = i; } DBG(("%d token", tokenPresent)); - DBG(("%u count", *pulCount)); + DBG(("%lu count", *pulCount)); DOUT; return CKR_OK; @@ -164,17 +179,18 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetSlotInfo)( ) { DIN; - /* - * According to pcsc-lite, the format of a reader name is: - * name [interface] (serial) index slot - * http://ludovicrousseau.blogspot.se/2010/05/what-is-in-pcsc-reader-name.html - */ - ykpiv_get_reader_slot(piv_state, slotID, pInfo->slotDescription); // TODO: should be ' ' padded - strcpy(pInfo->manufacturerID, "ADD SLOT MANUFACTURER NAME HERE"); - pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE | CKF_HW_SLOT; // TODO: What for other brands? Query for token status? - DBG(("slotID %u, pInfo %s", slotID, pInfo->slotDescription)); + if (piv_state == NULL) + return CKR_CRYPTOKI_NOT_INITIALIZED; + if (slotID >= n_slots) + return CKR_ARGUMENTS_BAD; + + memcpy(pInfo, &slots[slotID].info, sizeof(CK_SLOT_INFO)); + + DBG(("slotID %lu, pInfo %s", slotID, pInfo->slotDescription)); + + DOUT; return CKR_OK; } @@ -194,33 +210,44 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)( if (piv_state == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; - ykpiv_get_reader_slot(piv_state, slotID, buf); - vid = get_vendor_id(buf); + if (slotID >= n_slots) + return CKR_ARGUMENTS_BAD; - if (vid == UNKNOWN) + vid = slots[slotID].vid; + + if (vid == UNKNOWN) { + DBG(("No support for token in slot %lu", slotID)); return CKR_TOKEN_NOT_RECOGNIZED; + } - vendor = get_vendor(vid); + if (!HAS_TOKEN(slotID)) { + DBG(("Slot %lu has no token inserted", slotID)); + return CKR_TOKEN_NOT_PRESENT; + } + + vendor = get_vendor(vid); // TODO: make a token field in slot_t ? memset(pInfo->label, ' ', sizeof(pInfo->label)); - p = vendor.get_label(); + p = vendor.get_token_label(); len = strlen(p); strncpy(pInfo->label, p, len); memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID)); - p = vendor.get_manufacturer(); + p = vendor.get_token_manufacturer(); len = strlen(p); strncpy(pInfo->manufacturerID, p, len); memset(pInfo->model, ' ', sizeof(pInfo->model)); - p = vendor.get_model(); + p = vendor.get_token_model(); len = strlen(p); strncpy(pInfo->model, p, len); memset(pInfo->serialNumber, ' ', sizeof(pInfo->serialNumber)); - strncpy(pInfo->serialNumber, "12345", 5); + p = vendor.get_token_serial(); + len = strlen(p); + strncpy(pInfo->serialNumber, p, len); - pInfo->flags = vendor.get_flags(); // bit flags indicating capabilities and status of the device as defined below + pInfo->flags = vendor.get_token_flags(); // bit flags indicating capabilities and status of the device as defined below pInfo->ulMaxSessionCount = CK_UNAVAILABLE_INFORMATION; // TODO: should this be 1? @@ -230,9 +257,9 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)( pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; // number of read/write sessions that this application currently has open with the token - pInfo->ulMaxPinLen = PIV_MIN_PIN_LEN; // maximum length in bytes of the PIN + pInfo->ulMaxPinLen = PIV_MAX_PIN_LEN; // maximum length in bytes of the PIN - pInfo->ulMinPinLen = PIV_MAX_PIN_LEN; // minimum length in bytes of the PIN + pInfo->ulMinPinLen = PIV_MIN_PIN_LEN; // minimum length in bytes of the PIN pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; @@ -243,14 +270,15 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)( pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; ykpiv_get_version(piv_state, buf, sizeof(buf)); - ver = vendor.get_version(buf, strlen(buf)); + ver = vendor.get_token_version(buf, strlen(buf)); pInfo->hardwareVersion = ver; // version number of hardware pInfo->firmwareVersion = ver; // version number of firmware - memset(pInfo->utcTime, ' ', sizeof(pInfo->utcTime)); + memset(pInfo->utcTime, ' ', sizeof(pInfo->utcTime)); // No clock present, clear + DOUT; return CKR_OK; } @@ -273,7 +301,25 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismList)( ) { DIN; - DBG(("TODO!!!")); + + int i; + + if (pMechanismList == NULL_PTR) { + // Just return the number of mechanisms + *pulCount = 3; + DOUT; + return CKR_OK; + } + + if (*pulCount < 3) { + DBG(("Buffer too small: needed %lu, provided %lu", 1l, *pulCount)); + return CKR_BUFFER_TOO_SMALL; + } + + for (i = 0; i < 3; i++) { + pMechanismList[i] = CKM_SHA_1; + } + DOUT; return CKR_OK; } @@ -284,9 +330,9 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismInfo)( CK_MECHANISM_INFO_PTR pInfo ) { - DIN; - DBG(("TODO!!!")); - DOUT; + //DIN; + //DBG(("TODO!!!")); + //DOUT; return CKR_OK; } diff --git a/ykcs11/yubico.c b/ykcs11/yubico.c index 5f14296..7dfb61d 100644 --- a/ykcs11/yubico.c +++ b/ykcs11/yubico.c @@ -1,7 +1,58 @@ #include "yubico.h" #include "pkcs11.h" -CK_VERSION YUBICO_get_version(CK_UTF8CHAR_PTR version, CK_ULONG len) { + +CK_UTF8CHAR_PTR YUBICO_get_slot_description(void) { + + return "YubiKey Virtual Reader"; + +} + +CK_UTF8CHAR_PTR YUBICO_get_slot_manufacturer(void) { + + return "Yubico"; + +} + +CK_FLAGS YUBICO_get_slot_flags(void) { + + return CKF_TOKEN_PRESENT | CKF_HW_SLOT; + +} + +CK_VERSION YUBICO_get_slot_version(CK_UTF8CHAR_PTR version, CK_ULONG len) { + + CK_VERSION v = {1.0}; // Dummy value + + return v; + +} + +CK_UTF8CHAR_PTR YUBICO_get_token_label(void) { + + return "YubiKey PIV"; + +} + +CK_UTF8CHAR_PTR YUBICO_get_token_manufacturer(void) { + + return "Yubico"; + +} + +CK_UTF8CHAR_PTR YUBICO_get_token_model(void) { + + return "PRO"; + +} + +CK_FLAGS YUBICO_get_token_flags(void) { + + return CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; + +} + +CK_VERSION YUBICO_get_token_version(CK_UTF8CHAR_PTR version, CK_ULONG len) { CK_VERSION v = {0, 0}; int i = 0; @@ -28,26 +79,8 @@ CK_VERSION YUBICO_get_version(CK_UTF8CHAR_PTR version, CK_ULONG len) { return v; } -CK_UTF8CHAR_PTR YUBICO_get_label(void) { +CK_BYTE_PTR YUBICO_get_token_serial(void) { - return "YubiKey"; - -} - -CK_UTF8CHAR_PTR YUBICO_get_manufacturer(void) { - - return "Yubico"; - -} - -CK_UTF8CHAR_PTR YUBICO_get_model(void) { - - return "PRO"; - -} - -CK_FLAGS YUBICO_get_flags(void) { - - return CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; + return "1234"; } diff --git a/ykcs11/yubico.h b/ykcs11/yubico.h index cf78790..cdac771 100644 --- a/ykcs11/yubico.h +++ b/ykcs11/yubico.h @@ -3,10 +3,15 @@ #include "pkcs11.h" -CK_VERSION YUBICO_get_version(CK_UTF8CHAR_PTR version, CK_ULONG len); -CK_UTF8CHAR_PTR YUBICO_get_label(void); -CK_UTF8CHAR_PTR YUBICO_get_manufacturer(void); -CK_UTF8CHAR_PTR YUBICO_get_model(void); -CK_FLAGS YUBICO_get_flags(void); +CK_UTF8CHAR_PTR YUBICO_get_slot_description(void); +CK_UTF8CHAR_PTR YUBICO_get_slot_manufacturer(void); +CK_FLAGS YUBICO_get_slot_flags(void); +CK_VERSION YUBICO_get_slot_version(CK_UTF8CHAR_PTR version, CK_ULONG len); +CK_UTF8CHAR_PTR YUBICO_get_token_label(void); +CK_UTF8CHAR_PTR YUBICO_get_token_manufacturer(void); +CK_UTF8CHAR_PTR YUBICO_get_token_model(void); +CK_FLAGS YUBICO_get_token_flags(void); +CK_CHAR_PTR YUBICO_get_token_serial(void); +CK_VERSION YUBICO_get_token_version(CK_UTF8CHAR_PTR version, CK_ULONG len); #endif