#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_WINTYPES_H
# include <wintypes.h>
#endif
#ifdef HAVE_PCSCLITE_H
# include <pcsclite.h>
#endif
#ifdef HAVE_WINSCARD_H
# include <winscard.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_PTHREAD_H
# include <pthread.h>
#endif
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
#define HAVE_ERRNO_H 1
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_ZLIB_H
# ifdef HAVE_LIBZ
# include <zlib.h>
# endif
#else
# ifdef HAVE_LIBZ
# undef HAVE_LIBZ
# endif
#endif
#ifdef CACKEY_DEBUG_SEARCH_SPEEDTEST
# include <sys/time.h>
#endif
#define CK_PTR *
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#ifndef NULL_PTR
# define NULL_PTR 0
#endif
#include "pkcs11.h"
#include "pkcs11n.h"
#include "asn1-x509.h"
#include "sha1.h"
#include "md5.h"
#ifndef CACKEY_CRYPTOKI_VERSION_CODE
# define CACKEY_CRYPTOKI_VERSION_CODE 0x021e00
#endif
/* GSC-IS v2.1 Definitions */
/** Classes **/
#define GSCIS_CLASS_ISO7816 0x00
#define GSCIS_CLASS_GLOBAL_PLATFORM 0x80
/** Instructions **/
#define GSCIS_INSTR_GET_RESPONSE 0xC0
#define GSCIS_INSTR_READ_BINARY 0xB0
#define GSCIS_INSTR_UPDATE_BINARY 0xD6
#define GSCIS_INSTR_SELECT 0xA4
#define GSCIS_INSTR_EXTERNAL_AUTH 0x82
#define GSCIS_INSTR_GET_CHALLENGE 0x84
#define GSCIS_INSTR_INTERNAL_AUTH 0x88
#define GSCIS_INSTR_VERIFY 0x20
#define GSCIS_INSTR_CHANGE_REFERENCE 0x24
#define GSCIS_INSTR_SIGN 0x2A
#define GSCIS_INSTR_GET_PROP 0x56
#define GSCIS_INSTR_GET_ACR 0x4C
#define GSCIS_INSTR_READ_BUFFER 0x52
#define GSCIS_INSTR_SIGNDECRYPT 0x42
#define GSCIS_PARAM_SELECT_APPLET 0x04
/** Tags **/
/*** CCC Tags ***/
#define GSCIS_TAG_CARDID 0xF0
#define GSCIS_TAG_CCC_VER 0xF1
#define GSCIS_TAG_CCG_VER 0xF2
#define GSCIS_TAG_CARDURL 0xF3
#define GSCIS_TAG_PKCS15 0xF4
#define GSCIS_TAG_REG_DATA_MODEL 0xF5
#define GSCIS_TAG_ACR_TABLE 0xF6
#define GSCIS_TAG_CARD_APDU 0xF7
#define GSCIS_TAG_REDIRECTION 0xFA
#define GSCIS_TAG_CT 0xFB
#define GSCIS_TAG_ST 0xFC
#define GSCIS_TAG_NEXTCCC 0xFD
/*** General - EF 2200 ***/
#define GSCIS_TAG_FNAME 0x01
#define GSCIS_TAG_MNAME 0x02
#define GSCIS_TAG_LNAME 0x03
#define GSCIS_TAG_SUFFIX 0x04
#define GSCIS_TAG_GOVT_AGENCY 0x05
#define GSCIS_TAG_BUREAU 0x06
#define GSCIS_TAG_BUREAU_CODE 0x07
#define GSCIS_TAG_DEPT_CODE 0x08
#define GSCIS_TAG_TITLE 0x09
#define GSCIS_TAG_BUILDING 0x10
#define GSCIS_TAG_OFFICE_ADDR1 0x11
#define GSCIS_TAG_OFFICE_ADDR2 0x12
#define GSCIS_TAG_OFFICE_CITY 0x13
#define GSCIS_TAG_OFFICE_STATE 0x14
#define GSCIS_TAG_OFFICE_ZIP 0x15
#define GSCIS_TAG_OFFICE_COUNTRY 0x16
#define GSCIS_TAG_OFFICE_PHONE 0x17
#define GSCIS_TAG_OFFICE_PHONE_EXT 0x18
#define GSCIS_TAG_OFFICE_FAX 0x19
#define GSCIS_TAG_OFFICE_EMAIL 0x1A
#define GSCIS_TAG_OFFICE_ROOM 0x1B
#define GSCIS_TAG_NONGOV_AGENCY 0x1C
#define GSCIS_TAG_SSN_DESIGNATOR 0x1D
/*** PII - EF 2100 ***/
#define GSCIS_TAG_SSN 0x20
#define GSCIS_TAG_DOB 0x21
#define GSCIS_TAG_GENDER 0x22
/*** Login Information - EF 4000 ***/
#define GSCIS_TAG_USERID 0x40
#define GSCIS_TAG_DOMAIN 0x41
#define GSCIS_TAG_PASSWORD 0x42
/*** Card Information - EF 5000 ***/
#define GSCIS_TAG_ISSUERID 0x50
#define GSCIS_TAG_SERNO 0x51
#define GSCIS_TAG_ISSUE_DATE 0x52
#define GSCIS_TAG_EXPIRE_DATE 0x53
#define GSCIS_TAG_CARD_TYPE 0x54
#define GSCIS_TAG_SECURITY_CODE 0x57
#define GSCIS_TAG_CARDID_AID 0x58
/*** PIV Codes ***/
#define NISTSP800_73_3_INSTR_GET_DATA 0xCB
#define NISTSP800_73_3_INSTR_GENAUTH 0x87
/*** PKI Information - EF 7000 ***/
#define GSCIS_TAG_CERTIFICATE 0x70
#define GSCIS_TAG_CERT_ISSUE_DATE 0x71
#define GSCIS_TAG_CERT_EXPIRE_DATE 0x72
/** Applet IDs **/
#define GSCIS_AID_CCC 0xA0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00
#define GSCIS_AID_ID0 0xA0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
#define NISTSP800_73_3_PIV_AID 0xA0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00
/* PIV IDs */
/** Key Identifiers (NIST SP 800-78-3, Table 6-1 **/
#define NISTSP800_78_3_KEY_PIVAUTH 0x9A
#define NISTSP800_78_3_KEY_SIGNATURE 0x9C
#define NISTSP800_78_3_KEY_KEYMGT 0x9D
#define NISTSP800_78_3_KEY_CARDAUTH 0x9E
/** Algorithm Identifiers (NIST SP 800-78-3, Table 6-2 **/
#define NISTSP800_78_3_ALGO_RSA1024 0x06
#define NISTSP800_78_3_ALGO_RSA2048 0x07
/** Object Identifiers (NIST SP 800-73-3 Part 1, Table 2) **/
#define NISTSP800_73_3_OID_PIVAUTH 0x5F, 0xC1, 0x05
#define NISTSP800_73_3_OID_SIGNATURE 0x5F, 0xC1, 0x0A
#define NISTSP800_73_3_OID_KEYMGT 0x5F, 0xC1, 0x0B
#define NISTSP800_73_3_OID_CARDAUTH 0x5F, 0xC1, 0x01
/* Maximum size of data portion of APDUs */
/** Do not set this above 250 **/
#define CACKEY_APDU_MTU 250
/* ATR If not available */
#ifndef MAX_ATR_SIZE
#define MAX_ATR_SIZE 1024
#endif
#ifdef CACKEY_DEBUG
# ifdef HAVE_TIME_H
# include <time.h>
static time_t cackey_debug_start_time = 0;
static unsigned long CACKEY_DEBUG_GETTIME(void) {
if (cackey_debug_start_time == 0) {
cackey_debug_start_time = time(NULL);
}
return(time(NULL) - cackey_debug_start_time);
}
# else
static unsigned long CACKEY_DEBUG_GETTIME(void) {
return(0);
}
# endif
# define CACKEY_DEBUG_PRINTF(x...) { \
static char buf_user[4096] = {0}; \
snprintf(buf_user, sizeof(buf_user), x); \
buf_user[sizeof(buf_user) - 1] = '\0'; \
fprintf(cackey_debug_fd(), "[%lu]: %s():%i: %s\n", CACKEY_DEBUG_GETTIME(), __func__, __LINE__, buf_user); \
fflush(cackey_debug_fd()); \
}
# define CACKEY_DEBUG_PRINTBUF(f, x, y) { \
static char buf_user[8192] = {0}, *buf_user_p, *buf_user_print; \
unsigned long buf_user_size; \
unsigned char *TMPBUF; \
unsigned long idx; \
int snprintf_ret; \
TMPBUF = (unsigned char *) (x); \
buf_user[0] = 0; \
buf_user[2] = 0; \
buf_user_p = buf_user; \
buf_user_size = sizeof(buf_user); \
for (idx = 0; idx < (y); idx++) { \
if (buf_user_size <= 0) { \
break; \
}; \
snprintf_ret = snprintf(buf_user_p, buf_user_size, ", %02x", TMPBUF[idx]); \
if (snprintf_ret <= 0) { \
break; \
}; \
buf_user_p += snprintf_ret; \
buf_user_size -= snprintf_ret; \
}; \
buf_user[sizeof(buf_user) - 1] = '\0'; \
buf_user_print = buf_user + 2; \
fprintf(cackey_debug_fd(), "[%lu]: %s():%i: %s (%s/%lu = {%s})\n", CACKEY_DEBUG_GETTIME(), __func__, __LINE__, f, #x, (unsigned long) (y), buf_user_print); \
fflush(cackey_debug_fd()); \
}
# define free(x) { CACKEY_DEBUG_PRINTF("FREE(%p) (%s)", (void *) x, #x); free(x); }
static FILE *cackey_debug_fd(void) {
static FILE *fd = NULL;
char *logfile;
if (fd != NULL) {
return(fd);
}
/*
* Log to stderr initially so we can use debugging within
* this function without getting into an infinite loop
*/
fd = stderr;
logfile = getenv("CACKEY_DEBUG_LOGFILE");
if (logfile != NULL) {
CACKEY_DEBUG_PRINTF("Found environment variable: %s", logfile);
logfile = strchr(logfile, '=');
if (logfile == NULL) {
logfile = getenv("CACKEY_DEBUG_LOGFILE");
} else {
logfile++;
}
}
#ifdef CACKEY_DEBUG_LOGFILE
if (logfile == NULL) {
logfile = CACKEY_DEBUG_LOGFILE;
}
#endif
if (logfile != NULL) {
CACKEY_DEBUG_PRINTF("Found log file: %s", logfile);
fd = fopen(logfile, "a");
}
if (fd == NULL) {
fd = stderr;
}
if (fd == stderr) {
CACKEY_DEBUG_PRINTF("Returning stderr");
} else {
CACKEY_DEBUG_PRINTF("Returning %p", (void *) fd);
}
return(fd);
}
static void *CACKEY_DEBUG_FUNC_MALLOC(size_t size, const char *func, int line) {
void *retval;
retval = malloc(size);
fprintf(cackey_debug_fd(), "[%lu]: %s():%i: MALLOC() = %p\n", CACKEY_DEBUG_GETTIME(), func, line, retval);
fflush(cackey_debug_fd());
return(retval);
}
static void *CACKEY_DEBUG_FUNC_REALLOC(void *ptr, size_t size, const char *func, int line) {
void *retval;
retval = realloc(ptr, size);
if (retval != ptr) {
fprintf(cackey_debug_fd(), "[%lu]: %s():%i: REALLOC(%p) = %p\n", CACKEY_DEBUG_GETTIME(), func, line, ptr, retval);
fflush(cackey_debug_fd());
}
if (retval == NULL) {
CACKEY_DEBUG_PRINTF(" *** ERROR *** realloc returned NULL (size = %lu)", (unsigned long) size);
}
return(retval);
}
static char *CACKEY_DEBUG_FUNC_STRDUP(const char *ptr, const char *func, int line) {
char *retval;
retval = strdup(ptr);
fprintf(cackey_debug_fd(), "[%lu]: %s():%i: STRDUP_MALLOC() = %p\n", CACKEY_DEBUG_GETTIME(), func, line, retval);
fflush(cackey_debug_fd());
return(retval);
}
static const char *CACKEY_DEBUG_FUNC_TAG_TO_STR(unsigned char tag) {
switch (tag) {
case GSCIS_TAG_CARDID:
return("GSCIS_TAG_CARDID");
case GSCIS_TAG_CCC_VER:
return("GSCIS_TAG_CCC_VER");
case GSCIS_TAG_CCG_VER:
return("GSCIS_TAG_CCG_VER");
case GSCIS_TAG_CARDURL:
return("GSCIS_TAG_CARDURL");
case GSCIS_TAG_PKCS15:
return("GSCIS_TAG_PKCS15");
case GSCIS_TAG_REG_DATA_MODEL:
return("GSCIS_TAG_REG_DATA_MODEL");
case GSCIS_TAG_ACR_TABLE:
return("GSCIS_TAG_ACR_TABLE");
case GSCIS_TAG_CARD_APDU:
return("GSCIS_TAG_CARD_APDU");
case GSCIS_TAG_REDIRECTION:
return("GSCIS_TAG_REDIRECTION");
case GSCIS_TAG_CT:
return("GSCIS_TAG_CT");
case GSCIS_TAG_ST:
return("GSCIS_TAG_ST");
case GSCIS_TAG_NEXTCCC:
return("GSCIS_TAG_NEXTCCC");
case GSCIS_TAG_FNAME:
return("GSCIS_TAG_FNAME");
case GSCIS_TAG_MNAME:
return("GSCIS_TAG_MNAME");
case GSCIS_TAG_LNAME:
return("GSCIS_TAG_LNAME");
case GSCIS_TAG_SUFFIX:
return("GSCIS_TAG_SUFFIX");
case GSCIS_TAG_GOVT_AGENCY:
return("GSCIS_TAG_GOVT_AGENCY");
case GSCIS_TAG_BUREAU:
return("GSCIS_TAG_BUREAU");
case GSCIS_TAG_BUREAU_CODE:
return("GSCIS_TAG_BUREAU_CODE");
case GSCIS_TAG_DEPT_CODE:
return("GSCIS_TAG_DEPT_CODE");
case GSCIS_TAG_TITLE:
return("GSCIS_TAG_TITLE");
case GSCIS_TAG_BUILDING:
return("GSCIS_TAG_BUILDING");
case GSCIS_TAG_OFFICE_ADDR1:
return("GSCIS_TAG_OFFICE_ADDR1");
case GSCIS_TAG_OFFICE_ADDR2:
return("GSCIS_TAG_OFFICE_ADDR2");
case GSCIS_TAG_OFFICE_CITY:
return("GSCIS_TAG_OFFICE_CITY");
case GSCIS_TAG_OFFICE_STATE:
return("GSCIS_TAG_OFFICE_STATE");
case GSCIS_TAG_OFFICE_ZIP:
return("GSCIS_TAG_OFFICE_ZIP");
case GSCIS_TAG_OFFICE_COUNTRY:
return("GSCIS_TAG_OFFICE_COUNTRY");
case GSCIS_TAG_OFFICE_PHONE:
return("GSCIS_TAG_OFFICE_PHONE");
case GSCIS_TAG_OFFICE_PHONE_EXT:
return("GSCIS_TAG_OFFICE_PHONE_EXT");
case GSCIS_TAG_OFFICE_FAX:
return("GSCIS_TAG_OFFICE_FAX");
case GSCIS_TAG_OFFICE_EMAIL:
return("GSCIS_TAG_OFFICE_EMAIL");
case GSCIS_TAG_OFFICE_ROOM:
return("GSCIS_TAG_OFFICE_ROOM");
case GSCIS_TAG_NONGOV_AGENCY:
return("GSCIS_TAG_NONGOV_AGENCY");
case GSCIS_TAG_SSN_DESIGNATOR:
return("GSCIS_TAG_SSN_DESIGNATOR");
case GSCIS_TAG_SSN:
return("GSCIS_TAG_SSN");
case GSCIS_TAG_DOB:
return("GSCIS_TAG_DOB");
case GSCIS_TAG_GENDER:
return("GSCIS_TAG_GENDER");
case GSCIS_TAG_USERID:
return("GSCIS_TAG_USERID");
case GSCIS_TAG_DOMAIN:
return("GSCIS_TAG_DOMAIN");
case GSCIS_TAG_PASSWORD:
return("GSCIS_TAG_PASSWORD");
case GSCIS_TAG_ISSUERID:
return("GSCIS_TAG_ISSUERID");
case GSCIS_TAG_SERNO:
return("GSCIS_TAG_SERNO");
case GSCIS_TAG_ISSUE_DATE:
return("GSCIS_TAG_ISSUE_DATE");
case GSCIS_TAG_EXPIRE_DATE:
return("GSCIS_TAG_EXPIRE_DATE");
case GSCIS_TAG_CARD_TYPE:
return("GSCIS_TAG_CARD_TYPE");
case GSCIS_TAG_SECURITY_CODE:
return("GSCIS_TAG_SECURITY_CODE");
case GSCIS_TAG_CARDID_AID:
return("GSCIS_TAG_CARDID_AID");
case GSCIS_TAG_CERTIFICATE:
return("GSCIS_TAG_CERTIFICATE");
case GSCIS_TAG_CERT_ISSUE_DATE:
return("GSCIS_TAG_CERT_ISSUE_DATE");
case GSCIS_TAG_CERT_EXPIRE_DATE:
return("GSCIS_TAG_CERT_EXPIRE_DATE");
}
return("UNKNOWN");
}
static const char *CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(LONG retcode) {
switch (retcode) {
case SCARD_S_SUCCESS:
return("SCARD_S_SUCCESS");
case SCARD_E_CANCELLED:
return("SCARD_E_CANCELLED");
case SCARD_E_CANT_DISPOSE:
return("SCARD_E_CANT_DISPOSE");
case SCARD_E_INSUFFICIENT_BUFFER:
return("SCARD_E_INSUFFICIENT_BUFFER");
case SCARD_E_INVALID_ATR:
return("SCARD_E_INVALID_ATR");
case SCARD_E_INVALID_HANDLE:
return("SCARD_E_INVALID_HANDLE");
case SCARD_E_INVALID_PARAMETER:
return("SCARD_E_INVALID_PARAMETER");
case SCARD_E_INVALID_TARGET:
return("SCARD_E_INVALID_TARGET");
case SCARD_E_INVALID_VALUE:
return("SCARD_E_INVALID_VALUE");
case SCARD_E_NO_MEMORY:
return("SCARD_E_NO_MEMORY");
case SCARD_E_UNKNOWN_READER:
return("SCARD_E_UNKNOWN_READER");
case SCARD_E_TIMEOUT:
return("SCARD_E_TIMEOUT");
case SCARD_E_SHARING_VIOLATION:
return("SCARD_E_SHARING_VIOLATION");
case SCARD_E_NO_SMARTCARD:
return("SCARD_E_NO_SMARTCARD");
case SCARD_E_UNKNOWN_CARD:
return("SCARD_E_UNKNOWN_CARD");
case SCARD_E_PROTO_MISMATCH:
return("SCARD_E_PROTO_MISMATCH");
case SCARD_E_NOT_READY:
return("SCARD_E_NOT_READY");
case SCARD_E_SYSTEM_CANCELLED:
return("SCARD_E_SYSTEM_CANCELLED");
case SCARD_E_NOT_TRANSACTED:
return("SCARD_E_NOT_TRANSACTED");
case SCARD_E_READER_UNAVAILABLE:
return("SCARD_E_READER_UNAVAILABLE");
case SCARD_W_UNSUPPORTED_CARD:
return("SCARD_W_UNSUPPORTED_CARD");
case SCARD_W_UNRESPONSIVE_CARD:
return("SCARD_W_UNRESPONSIVE_CARD");
case SCARD_W_UNPOWERED_CARD:
return("SCARD_W_UNPOWERED_CARD");
case SCARD_W_RESET_CARD:
return("SCARD_W_RESET_CARD");
case SCARD_W_REMOVED_CARD:
return("SCARD_W_REMOVED_CARD");
case SCARD_E_PCI_TOO_SMALL:
return("SCARD_E_PCI_TOO_SMALL");
case SCARD_E_READER_UNSUPPORTED:
return("SCARD_E_READER_UNSUPPORTED");
case SCARD_E_DUPLICATE_READER:
return("SCARD_E_DUPLICATE_READER");
case SCARD_E_CARD_UNSUPPORTED:
return("SCARD_E_CARD_UNSUPPORTED");
case SCARD_E_NO_SERVICE:
return("SCARD_E_NO_SERVICE");
case SCARD_E_SERVICE_STOPPED:
return("SCARD_E_SERVICE_STOPPED");
case SCARD_E_UNSUPPORTED_FEATURE:
return("SCARD_E_UNSUPPORTED_FEATURE");
#ifdef SCARD_W_INSERTED_CARD
case SCARD_W_INSERTED_CARD:
return("SCARD_W_INSERTED_CARD");
#endif
#ifdef SCARD_E_NO_READERS_AVAILABLE
case SCARD_E_NO_READERS_AVAILABLE:
return("SCARD_E_NO_READERS_AVAILABLE");
#endif
}
return("UNKNOWN");
}
static const char *CACKEY_DEBUG_FUNC_OBJID_TO_STR(uint16_t objid) {
switch (objid) {
case 0x2000:
return("CACKEY_TLV_OBJID_GENERALINFO");
case 0x2100:
return("CACKEY_TLV_OBJID_PROPERSONALINFO");
case 0x3000:
return("CACKEY_TLV_OBJID_ACCESSCONTROL");
case 0x4000:
return("CACKEY_TLV_OBJID_LOGIN");
case 0x5000:
return("CACKEY_TLV_OBJID_CARDINFO");
case 0x6000:
return("CACKEY_TLV_OBJID_BIOMETRICS");
case 0x7000:
return("CACKEY_TLV_OBJID_DIGITALSIGCERT");
case 0x0200:
return("CACKEY_TLV_OBJID_CAC_PERSON");
case 0x0202:
return("CACKEY_TLV_OBJID_CAC_BENEFITS");
case 0x0203:
return("CACKEY_TLV_OBJID_CAC_OTHERBENEFITS");
case 0x0201:
return("CACKEY_TLV_OBJID_CAC_PERSONNEL");
case 0x02FE:
return("CACKEY_TLV_OBJID_CAC_PKICERT");
}
return("UNKNOWN");
}
static const char *CACKEY_DEBUG_FUNC_APPTYPE_TO_STR(uint8_t apptype) {
switch (apptype) {
case 0x00:
return("NONE");
case 0x01:
return("CACKEY_TLV_APP_GENERIC");
case 0x02:
return("CACKEY_TLV_APP_SKI");
case 0x03:
return("CACKEY_TLV_APP_GENERIC | CACKEY_TLV_APP_SKI");
case 0x04:
return("CACKEY_TLV_APP_PKI");
case 0x05:
return("CACKEY_TLV_APP_GENERIC | CACKEY_TLV_APP_PKI");
case 0x06:
return("CACKEY_TLV_APP_SKI | CACKEY_TLV_APP_PKI");
case 0x07:
return("CACKEY_TLV_APP_GENERIC | CACKEY_TLV_APP_SKI | CACKEY_TLV_APP_PKI");
}
return("INVALID");
}
static const char *CACKEY_DEBUG_FUNC_ATTRIBUTE_TO_STR(CK_ATTRIBUTE_TYPE attr) {
switch (attr) {
case CKA_CLASS:
return("CKA_CLASS");
case CKA_TOKEN:
return("CKA_TOKEN");
case CKA_PRIVATE:
return("CKA_PRIVATE");
case CKA_LABEL:
return("CKA_LABEL");
case CKA_APPLICATION:
return("CKA_APPLICATION");
case CKA_VALUE:
return("CKA_VALUE");
case CKA_OBJECT_ID:
return("CKA_OBJECT_ID");
case CKA_CERTIFICATE_TYPE:
return("CKA_CERTIFICATE_TYPE");
case CKA_ISSUER:
return("CKA_ISSUER");
case CKA_SERIAL_NUMBER:
return("CKA_SERIAL_NUMBER");
case CKA_AC_ISSUER:
return("CKA_AC_ISSUER");
case CKA_OWNER:
return("CKA_OWNER");
case CKA_ATTR_TYPES:
return("CKA_ATTR_TYPES");
case CKA_TRUSTED:
return("CKA_TRUSTED");
case CKA_KEY_TYPE:
return("CKA_KEY_TYPE");
case CKA_SUBJECT:
return("CKA_SUBJECT");
case CKA_ID:
return("CKA_ID");
case CKA_SENSITIVE:
return("CKA_SENSITIVE");
case CKA_ENCRYPT:
return("CKA_ENCRYPT");
case CKA_DECRYPT:
return("CKA_DECRYPT");
case CKA_WRAP:
return("CKA_WRAP");
case CKA_UNWRAP:
return("CKA_UNWRAP");
case CKA_SIGN:
return("CKA_SIGN");
case CKA_SIGN_RECOVER:
return("CKA_SIGN_RECOVER");
case CKA_VERIFY:
return("CKA_VERIFY");
case CKA_VERIFY_RECOVER:
return("CKA_VERIFY_RECOVER");
case CKA_DERIVE:
return("CKA_DERIVE");
case CKA_START_DATE:
return("CKA_START_DATE");
case CKA_END_DATE:
return("CKA_END_DATE");
case CKA_MODULUS:
return("CKA_MODULUS");
case CKA_MODULUS_BITS:
return("CKA_MODULUS_BITS");
case CKA_PUBLIC_EXPONENT:
return("CKA_PUBLIC_EXPONENT");
case CKA_PRIVATE_EXPONENT:
return("CKA_PRIVATE_EXPONENT");
case CKA_PRIME_1:
return("CKA_PRIME_1");
case CKA_PRIME_2:
return("CKA_PRIME_2");
case CKA_EXPONENT_1:
return("CKA_EXPONENT_1");
case CKA_EXPONENT_2:
return("CKA_EXPONENT_2");
case CKA_COEFFICIENT:
return("CKA_COEFFICIENT");
case CKA_PRIME:
return("CKA_PRIME");
case CKA_SUBPRIME:
return("CKA_SUBPRIME");
case CKA_BASE:
return("CKA_BASE");
case CKA_PRIME_BITS:
return("CKA_PRIME_BITS");
case CKA_SUB_PRIME_BITS:
return("CKA_SUB_PRIME_BITS");
case CKA_VALUE_BITS:
return("CKA_VALUE_BITS");
case CKA_VALUE_LEN:
return("CKA_VALUE_LEN");
case CKA_EXTRACTABLE:
return("CKA_EXTRACTABLE");
case CKA_LOCAL:
return("CKA_LOCAL");
case CKA_NEVER_EXTRACTABLE:
return("CKA_NEVER_EXTRACTABLE");
case CKA_ALWAYS_SENSITIVE:
return("CKA_ALWAYS_SENSITIVE");
case CKA_KEY_GEN_MECHANISM:
return("CKA_KEY_GEN_MECHANISM");
case CKA_MODIFIABLE:
return("CKA_MODIFIABLE");
case CKA_ECDSA_PARAMS:
return("CKA_ECDSA_PARAMS");
case CKA_EC_POINT:
return("CKA_EC_POINT");
case CKA_SECONDARY_AUTH:
return("CKA_SECONDARY_AUTH");
case CKA_AUTH_PIN_FLAGS:
return("CKA_AUTH_PIN_FLAGS");
case CKA_HW_FEATURE_TYPE:
return("CKA_HW_FEATURE_TYPE");
case CKA_RESET_ON_INIT:
return("CKA_RESET_ON_INIT");
case CKA_HAS_RESET:
return("CKA_HAS_RESET");
case CKA_VENDOR_DEFINED:
return("CKA_VENDOR_DEFINED");
}
return("UNKNOWN");
}
# define malloc(x) CACKEY_DEBUG_FUNC_MALLOC(x, __func__, __LINE__)
# define realloc(x, y) CACKEY_DEBUG_FUNC_REALLOC(x, y, __func__, __LINE__)
# ifdef strdup
# undef strdup
# endif
# define strdup(x) CACKEY_DEBUG_FUNC_STRDUP(x, __func__, __LINE__)
#else
# define CACKEY_DEBUG_PRINTF(x...) /**/
# define CACKEY_DEBUG_PRINTBUF(f, x, y) /**/
# define CACKEY_DEBUG_FUNC_TAG_TO_STR(x) "DEBUG_DISABLED"
# define CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(x) "DEBUG_DISABLED"
# define CACKEY_DEBUG_FUNC_OBJID_TO_STR(x) "DEBUG_DISABLED"
# define CACKEY_DEBUG_FUNC_APPTYPE_TO_STR(x) "DEBUG_DISABLED"
# define CACKEY_DEBUG_FUNC_ATTRIBUTE_TO_STR(x) "DEBUG_DISABLED"
#endif
/*
* Include these source files in this translation unit so that we can bind to
* functions and not include any symbols in the output shared object.
*/
#include "asn1-x509.c"
#include "sha1.c"
#include "md5.c"
typedef enum {
CACKEY_ID_TYPE_ERROR,
CACKEY_ID_TYPE_UNKNOWN,
CACKEY_ID_TYPE_CAC,
CACKEY_ID_TYPE_PIV,
CACKEY_ID_TYPE_CERT_ONLY
} cackey_pcsc_id_type;
struct cackey_pcsc_identity {
cackey_pcsc_id_type id_type;
size_t certificate_len;
unsigned char *certificate;
ssize_t keysize;
union {
struct {
unsigned char applet[7];
uint16_t file;
} cac;
struct {
unsigned char key_id;
char label[32];
} piv;
} card;
};
struct cackey_identity {
struct cackey_pcsc_identity *pcsc_identity;
CK_ATTRIBUTE *attributes;
CK_ULONG attributes_count;
};
struct cackey_session {
int active;
CK_SLOT_ID slotID;
CK_STATE state;
CK_FLAGS flags;
CK_ULONG ulDeviceError;
CK_VOID_PTR pApplication;
CK_NOTIFY Notify;
struct cackey_identity *identities;
unsigned long identities_count;
int search_active;
CK_ATTRIBUTE_PTR search_query;
CK_ULONG search_query_count;
unsigned long search_curr_id;
int sign_active;
CK_MECHANISM_TYPE sign_mechanism;
CK_BYTE_PTR sign_buf;
unsigned long sign_buflen;
unsigned long sign_bufused;
struct cackey_identity *sign_identity;
int decrypt_active;
CK_MECHANISM_TYPE decrypt_mechanism;
CK_VOID_PTR decrypt_mech_parm;
CK_ULONG decrypt_mech_parmlen;
struct cackey_identity *decrypt_identity;
};
struct cackey_slot {
int active;
int internal;
char *pcsc_reader;
int pcsc_card_connected;
SCARDHANDLE pcsc_card;
int transaction_depth;
int transaction_need_hw_lock;
int slot_reset;
CK_FLAGS token_flags;
unsigned char *label;
DWORD protocol;
unsigned int cached_certs_count;
struct cackey_pcsc_identity *cached_certs;
cackey_pcsc_id_type id_type_hint;
};
typedef enum {
CACKEY_TLV_APP_GENERIC = 0x01,
CACKEY_TLV_APP_SKI = 0x02,
CACKEY_TLV_APP_PKI = 0x04
} cackey_tlv_apptype;
typedef enum {
CACKEY_TLV_OBJID_GENERALINFO = 0x2000,
CACKEY_TLV_OBJID_PROPERSONALINFO = 0x2100,
CACKEY_TLV_OBJID_ACCESSCONTROL = 0x3000,
CACKEY_TLV_OBJID_LOGIN = 0x4000,
CACKEY_TLV_OBJID_CARDINFO = 0x5000,
CACKEY_TLV_OBJID_BIOMETRICS = 0x6000,
CACKEY_TLV_OBJID_DIGITALSIGCERT = 0x7000,
CACKEY_TLV_OBJID_CAC_PERSON = 0x0200,
CACKEY_TLV_OBJID_CAC_BENEFITS = 0x0202,
CACKEY_TLV_OBJID_CAC_OTHERBENEFITS = 0x0203,
CACKEY_TLV_OBJID_CAC_PERSONNEL = 0x0201,
CACKEY_TLV_OBJID_CAC_PKICERT = 0x02FE
} cackey_tlv_objectid;
typedef enum {
CACKEY_PCSC_S_TOKENPRESENT = 1,
CACKEY_PCSC_S_OK = 0,
CACKEY_PCSC_E_GENERIC = -1,
CACKEY_PCSC_E_BADPIN = -2,
CACKEY_PCSC_E_LOCKED = -3,
CACKEY_PCSC_E_NEEDLOGIN = -4,
CACKEY_PCSC_E_TOKENABSENT = -6,
CACKEY_PCSC_E_RETRY = -7,
CACKEY_PCSC_E_NODATA = -8
} cackey_ret;
struct cackey_tlv_cardurl {
unsigned char rid[5];
cackey_tlv_apptype apptype;
cackey_tlv_objectid objectid;
cackey_tlv_objectid appid;
unsigned char pinid;
};
struct cackey_tlv_entity;
struct cackey_tlv_entity {
uint8_t tag;
size_t length;
union {
void *value;
struct cackey_tlv_cardurl *value_cardurl;
uint8_t value_byte;
};
struct cackey_tlv_entity *_next;
};
/* CACKEY Global Handles */
static void *cackey_biglock = NULL;
static struct cackey_session cackey_sessions[128];
static struct cackey_slot cackey_slots[128];
static int cackey_initialized = 0;
static int cackey_biglock_init = 0;
CK_C_INITIALIZE_ARGS cackey_args;
/** Extra certificates to include in token **/
struct cackey_pcsc_identity extra_certs[] = {
#include "cackey_builtin_certs.h"
};
#define CACKEY_MACRO_DEFAULT_XSTR(str) CACKEY_MACRO_DEFAULT_STR(str)
#define CACKEY_MACRO_DEFAULT_STR(str) #str
/* Protected Authentication Path command */
static char *cackey_pin_command = NULL;
/* Reader Exclusion or Include-only */
static char *cackey_readers_include_only = NULL;
static char *cackey_readers_exclude = NULL;
/* PCSC Global Handles */
static LPSCARDCONTEXT cackey_pcsc_handle = NULL;
static unsigned long cackey_getversion(void) {
static unsigned long retval = 255;
unsigned long major = 0;
unsigned long minor = 0;
char *major_str = NULL;
char *minor_str = NULL;
CACKEY_DEBUG_PRINTF("Called.");
if (retval != 255) {
CACKEY_DEBUG_PRINTF("Returning 0x%lx (cached).", retval);
return(retval);
}
retval = 0;
#ifdef PACKAGE_VERSION
major_str = PACKAGE_VERSION;
if (major_str) {
major = strtoul(major_str, &minor_str, 10);
if (minor_str) {
minor = strtoul(minor_str + 1, NULL, 10);
}
}
retval = (major << 16) | (minor << 8);
#endif
CACKEY_DEBUG_PRINTF("Returning 0x%lx", retval);
return(retval);
}
/* PC/SC Related Functions */
/*
* SYNPOSIS
* void cackey_slots_disconnect_all(int unitialize_all_readers);
*
* ARGUMENTS
* int unitialize_all_readers Free the "pcsc_reader" object associated with
* each slot (boolean)
*
* RETURN VALUE
* None
*
* NOTES
* This function disconnects from all cards.
*
*/
static void cackey_slots_disconnect_all(int unitialize_all_readers) {
uint32_t idx;
CACKEY_DEBUG_PRINTF("Called.");
for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) {
if (cackey_slots[idx].internal) {
/* Skip internal slots */
continue;
}
if (cackey_slots[idx].pcsc_card_connected) {
CACKEY_DEBUG_PRINTF("SCardDisconnect(%lu) called", (unsigned long) idx);
SCardDisconnect(cackey_slots[idx].pcsc_card, SCARD_LEAVE_CARD);
}
if (cackey_slots[idx].label) {
free(cackey_slots[idx].label);
cackey_slots[idx].label = NULL;
}
if (unitialize_all_readers || !cackey_slots[idx].active) {
if (cackey_slots[idx].pcsc_reader) {
free(cackey_slots[idx].pcsc_reader);
cackey_slots[idx].pcsc_reader = NULL;
}
cackey_slots[idx].transaction_need_hw_lock = 0;
cackey_slots[idx].transaction_depth = 0;
cackey_slots[idx].id_type_hint = CACKEY_ID_TYPE_UNKNOWN;
} else {
if (cackey_slots[idx].transaction_depth > 0) {
cackey_slots[idx].transaction_need_hw_lock = 1;
}
}
cackey_slots[idx].pcsc_card_connected = 0;
if (cackey_slots[idx].active) {
CACKEY_DEBUG_PRINTF("Marking active slot %lu as being reset", (unsigned long) idx);
cackey_slots[idx].slot_reset = 1;
}
}
CACKEY_DEBUG_PRINTF("Returning");
return;
}
/*
* SYNPOSIS
* cackey_ret cackey_pcsc_connect(void);
*
* ARGUMENTS
* None
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* This function connects to the PC/SC Connection Manager and updates the
* global handle.
*
*/
static cackey_ret cackey_pcsc_connect(void) {
LONG scard_est_context_ret;
#ifdef HAVE_SCARDISVALIDCONTEXT
LONG scard_isvalid_ret;
#endif
CACKEY_DEBUG_PRINTF("Called.");
if (cackey_pcsc_handle == NULL) {
cackey_pcsc_handle = malloc(sizeof(*cackey_pcsc_handle));
if (cackey_pcsc_handle == NULL) {
CACKEY_DEBUG_PRINTF("Call to malloc() failed, returning in failure");
cackey_slots_disconnect_all(0);
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("SCardEstablishContext() called");
scard_est_context_ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, cackey_pcsc_handle);
if (scard_est_context_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Call to SCardEstablishContext failed (returned %s/%li), returning in failure", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_est_context_ret), (long) scard_est_context_ret);
free(cackey_pcsc_handle);
cackey_pcsc_handle = NULL;
cackey_slots_disconnect_all(0);
return(CACKEY_PCSC_E_GENERIC);
}
}
#ifdef HAVE_SCARDISVALIDCONTEXT
CACKEY_DEBUG_PRINTF("SCardIsValidContext() called");
scard_isvalid_ret = SCardIsValidContext(*cackey_pcsc_handle);
if (scard_isvalid_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Handle has become invalid (SCardIsValidContext = %s/%li), trying to re-establish...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_isvalid_ret), (long) scard_isvalid_ret);
CACKEY_DEBUG_PRINTF("SCardEstablishContext() called");
scard_est_context_ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, cackey_pcsc_handle);
if (scard_est_context_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Call to SCardEstablishContext failed (returned %s/%li), returning in failure", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_est_context_ret), (long) scard_est_context_ret);
free(cackey_pcsc_handle);
cackey_pcsc_handle = NULL;
cackey_slots_disconnect_all(0);
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("Handle has been re-established");
}
#endif
CACKEY_DEBUG_PRINTF("Sucessfully connected to PC/SC, returning in success");
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* cackey_ret cackey_pcsc_disconnect(void);
*
* ARGUMENTS
* None
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* This function disconnects from the PC/SC Connection manager and updates
* the global handle.
*
*/
static cackey_ret cackey_pcsc_disconnect(void) {
LONG scard_rel_context_ret;
CACKEY_DEBUG_PRINTF("Called.");
if (cackey_pcsc_handle == NULL) {
return(CACKEY_PCSC_S_OK);
}
scard_rel_context_ret = SCardReleaseContext(*cackey_pcsc_handle);
if (cackey_pcsc_handle) {
free(cackey_pcsc_handle);
cackey_pcsc_handle = NULL;
}
if (scard_rel_context_ret != SCARD_S_SUCCESS) {
return(CACKEY_PCSC_E_GENERIC);
}
cackey_slots_disconnect_all(0);
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* void cackey_mark_slot_reset(struct cackey_slot *slot);
*
* ARGUMENTS
* None
*
* RETURN VALUE
* None
*
* NOTES
* This function marks a slot has having been reset, to later be cleaned up.
* Cleanup only happens when a PKCS#11 client calls C_FindObjectsInit.
*
*/
static void cackey_mark_slot_reset(struct cackey_slot *slot) {
struct cackey_pcsc_identity *pcsc_identities;
unsigned long num_certs;
if (slot == NULL) {
return;
}
CACKEY_DEBUG_PRINTF("Called.");
if (slot->pcsc_card_connected) {
SCardDisconnect(slot->pcsc_card, SCARD_LEAVE_CARD);
}
slot->slot_reset = 1;
slot->pcsc_card_connected = 0;
if (cackey_pin_command == NULL) {
slot->token_flags = CKF_LOGIN_REQUIRED;
} else {
slot->token_flags = 0;
}
return;
}
/*
* SYNPOSIS
* LONG cackey_reconnect_card(struct cackey_slot *slot, DWORD default_protocol);
*
* ARGUMENTS
* cackey_slot *slot
* Slot to send commands to
*
* DWORD default_protocol
* Protocol to attempt first
*
* RETURN VALUE
* The return value from SCardReconnect()
*
* NOTES
* This function is a wrapper around SCardReconnect()
*
* The SCardReconnect() function call will be called first with the
* dwPreferredProtocols of "default_protocol". If that call returns
* SCARD_E_PROTO_MISMATCH try again with a protocol of T=0, and failing
* that T=1.
*
*/
static LONG cackey_reconnect_card(struct cackey_slot *slot, DWORD default_protocol) {
DWORD selected_protocol;
LONG scard_conn_ret;
selected_protocol = 0;
scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, default_protocol, SCARD_RESET_CARD, &selected_protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardReconnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0")
scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, SCARD_RESET_CARD, &selected_protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardReconnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1")
scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &selected_protocol);
}
}
if (scard_conn_ret == SCARD_S_SUCCESS) {
slot->protocol = selected_protocol;
}
return(scard_conn_ret);
}
/*
* SYNPOSIS
* cackey_ret cackey_connect_card(struct cackey_slot *slot);
*
* ARGUMENTS
* cackey_slot *slot
* Slot to send commands to
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* None
*
*/
static cackey_ret cackey_connect_card(struct cackey_slot *slot) {
cackey_ret pcsc_connect_ret;
DWORD protocol;
LONG scard_conn_ret;
CACKEY_DEBUG_PRINTF("Called.");
if (!slot) {
CACKEY_DEBUG_PRINTF("Invalid slot specified, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
pcsc_connect_ret = cackey_pcsc_connect();
if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Connection to PC/SC failed, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
/* Connect to reader, if needed */
if (!slot->pcsc_card_connected) {
CACKEY_DEBUG_PRINTF("SCardConnect(%s) called for slot %p", slot->pcsc_reader, slot);
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0")
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &slot->pcsc_card, &protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1")
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
}
}
if (scard_conn_ret == SCARD_W_UNPOWERED_CARD) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_W_UNPOWERED_CARD, trying to re-connect...");
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0")
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &slot->pcsc_card, &protocol);
if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1")
scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol);
}
}
scard_conn_ret = cackey_reconnect_card(slot, protocol);
}
if (scard_conn_ret == SCARD_E_NO_SERVICE) {
CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_NO_SERVICE -- which could mean our handle is invalid, will try to reconnect to PC/SC service");
cackey_pcsc_disconnect();
cackey_pcsc_connect();
cackey_mark_slot_reset(slot);
return(cackey_connect_card(slot));
}
if (scard_conn_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Connection to card failed, returning in failure (SCardConnect() = %s/%li)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_conn_ret), (long) scard_conn_ret);
return(CACKEY_PCSC_E_GENERIC);
}
slot->pcsc_card_connected = 1;
slot->transaction_depth = 0;
slot->transaction_need_hw_lock = 0;
slot->protocol = protocol;
}
CACKEY_DEBUG_PRINTF("Returning in success");
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* cackey_ret cackey_begin_transaction(struct cackey_slot *slot);
*
* ARGUMENTS
* cackey_slot *slot
* Slot to send commands to
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* The transaction should be terminated using "cackey_end_transaction"
*
*/
static cackey_ret cackey_begin_transaction(struct cackey_slot *slot) {
cackey_ret cackey_conn_ret;
LONG scard_trans_ret;
CACKEY_DEBUG_PRINTF("Called.");
cackey_conn_ret = cackey_connect_card(slot);
if (cackey_conn_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable to connect to card, returning in error");
return(CACKEY_PCSC_E_GENERIC);
}
slot->transaction_depth++;
if (slot->transaction_depth > 1 && !slot->transaction_need_hw_lock) {
CACKEY_DEBUG_PRINTF("Already in a transaction, performing no action (new depth = %i)", slot->transaction_depth);
return(CACKEY_PCSC_S_OK);
}
slot->transaction_need_hw_lock = 0;
scard_trans_ret = SCardBeginTransaction(slot->pcsc_card);
if (scard_trans_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Unable to begin transaction, returning in error");
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("Sucessfully began transaction on slot (%s)", slot->pcsc_reader);
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* cackey_ret cackey_end_transaction(struct cackey_slot *slot);
*
* ARGUMENTS
* cackey_slot *slot
* Slot to send commands to
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* This function requires "cackey_begin_transaction" to be called first
*
*/
static cackey_ret cackey_end_transaction(struct cackey_slot *slot) {
LONG scard_trans_ret;
CACKEY_DEBUG_PRINTF("Called.");
if (!slot->pcsc_card_connected) {
CACKEY_DEBUG_PRINTF("Card is not connected, unable to end transaction on card");
if (slot->transaction_depth > 0) {
CACKEY_DEBUG_PRINTF("Decreasing transaction depth and asking for a hardware lock on the next begin transaction (current depth = %i)", slot->transaction_depth);
slot->transaction_depth--;
if (slot->transaction_depth > 0) {
slot->transaction_need_hw_lock = 1;
}
}
return(CACKEY_PCSC_E_GENERIC);
}
if (slot->transaction_depth == 0) {
CACKEY_DEBUG_PRINTF("Terminating a transaction that has not begun!");
return(CACKEY_PCSC_E_GENERIC);
}
slot->transaction_depth--;
if (slot->transaction_depth > 0) {
CACKEY_DEBUG_PRINTF("Transactions still in progress, not terminating on-card Transaction (current depth = %i)", slot->transaction_depth);
return(CACKEY_PCSC_S_OK);
}
scard_trans_ret = SCardEndTransaction(slot->pcsc_card, SCARD_LEAVE_CARD);
if (scard_trans_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Unable to end transaction, returning in error");
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("Sucessfully terminated transaction on slot (%s)", slot->pcsc_reader);
return(CACKEY_PCSC_S_OK);
}
/* APDU Related Functions */
/*
* SYNPOSIS
* cackey_ret cackey_send_apdu(struct cackey_slot *slot, unsigned char class, unsigned char instruction, unsigned char p1, unsigned char p2, unsigned char lc, unsigned char *data, unsigned char le, uint16_t *respcode, unsigned char *respdata, size_t *respdata_len);
*
* ARGUMENTS
* cackey_slot *slot
* Slot to send commands to
*
* unsigned char class
* APDU Class (GSCIS_CLASS_ISO7816 or GSCIS_CLASS_GLOBAL_PLATFORM
* usually), (CLA)
*
* unsigned char instruction
* APDU Instruction (INS)
*
* unsigned char p1
* APDU Parameter 1 (P1)
*
* unsigned char p2
* APDU Parameter 2 (P2)
*
* unsigned char lc
* APDU Length of Content (Lc) -- this is the length of "data"
* parameter. If "data" is specified as NULL, this parameter will
* be ignored.
*
* unsigned char *data
* Pointer to buffer to send. It should be "Lc" bytes long. If
* specified as NULL, "Lc" will not be sent, and this buffer will be
* ignored.
*
* unsigned char le
* APDU Length of Expectation (Le) -- this is the length of the
* expected reply. If this is specified as 0 then it will not
* be sent.
*
* uint16_t *respcode
* [OUT] Pointer to storage of APDU response code. If this is
* specified as NULL, the response code will be discarded.
*
* unsigned char *respdata
* [OUT] Pointer to storage of APDU response data. If this is
* specified as NULL, the response data will be discarded. If
* the "respdata_len" parameter is specified as NULL, this buffer
* will not be updated.
*
* size_t *respdata_len
* [IN, OUT] Pointer initialing containing the size of the "respdata"
* buffer. Before returning, the pointed to value is updated to the
* number of bytes written to the buffer. If this is specified as
* NULL, it will not be updated, and "respdata" will be ignored causing
* the response data to be discarded.
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
* CACKEY_PCSC_E_TOKENABSENT If the sending failed because the token is
* absent
* CACKEY_PCSC_E_RETRY If something that looks retry'able went
* wrong -- try the whole transaction over
* again
*
* NOTES
* This function will connect to the PC/SC Connection Manager via
* cackey_pcsc_connect() if needed.
*
* It will connect to the card in the reader attached to the slot
* specified. It will reconnect to the card if the connection
* goes away.
*
*/
static cackey_ret cackey_send_apdu(struct cackey_slot *slot, unsigned char class, unsigned char instruction, unsigned char p1, unsigned char p2, unsigned int lc, unsigned char *data, unsigned int le, uint16_t *respcode, unsigned char *respdata, size_t *respdata_len) {
uint8_t major_rc, minor_rc;
size_t bytes_to_copy, tmp_respdata_len;
LPCSCARD_IO_REQUEST pioSendPci;
SCARD_IO_REQUEST pioRecvPci;
DWORD xmit_len, recv_len;
LONG scard_xmit_ret, scard_reconn_ret;
BYTE xmit_buf[1024], recv_buf[1024];
int pcsc_connect_ret, pcsc_getresp_ret;
int idx;
CACKEY_DEBUG_PRINTF("Called.");
if (!slot) {
CACKEY_DEBUG_PRINTF("Invalid slot specified.");
return(CACKEY_PCSC_E_GENERIC);
}
if (respcode) {
*respcode = 0xffff;
}
pcsc_connect_ret = cackey_connect_card(slot);
if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable to connect to card, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
/* Determine which protocol to send using */
switch (slot->protocol) {
case SCARD_PROTOCOL_T0:
CACKEY_DEBUG_PRINTF("Protocol to send datagram is T=0");
pioSendPci = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
CACKEY_DEBUG_PRINTF("Protocol to send datagram is T=1");
pioSendPci = SCARD_PCI_T1;
break;
default:
CACKEY_DEBUG_PRINTF("Invalid protocol found, aborting.");
return(CACKEY_PCSC_E_GENERIC);
}
/* Transmit */
xmit_len = 0;
xmit_buf[xmit_len++] = class;
xmit_buf[xmit_len++] = instruction;
xmit_buf[xmit_len++] = p1;
xmit_buf[xmit_len++] = p2;
if (data) {
if (lc > 255) {
CACKEY_DEBUG_PRINTF("CAUTION! Using an Lc greater than 255 is untested. Lc = %u", lc);
xmit_buf[xmit_len++] = 0x82; /* XXX UNTESTED */
xmit_buf[xmit_len++] = (lc & 0xff00) >> 8;
xmit_buf[xmit_len++] = lc & 0xff;
} else {
xmit_buf[xmit_len++] = lc;
}
for (idx = 0; idx < lc; idx++) {
xmit_buf[xmit_len++] = data[idx];
}
}
if (le != 0x00) {
if (le > 256) {
CACKEY_DEBUG_PRINTF("CAUTION! Using an Le greater than 256 is untested. Le = %u", le);
xmit_buf[xmit_len++] = 0x82; /* XXX UNTESTED */
xmit_buf[xmit_len++] = (le & 0xff00) >> 8;
xmit_buf[xmit_len++] = le & 0xff;
} else if (le == 256) {
xmit_buf[xmit_len++] = 0x00;
} else {
xmit_buf[xmit_len++] = le;
}
}
/* Begin Smartcard Transaction */
cackey_begin_transaction(slot);
if (class == GSCIS_CLASS_ISO7816 && (instruction == GSCIS_INSTR_VERIFY || instruction == GSCIS_INSTR_CHANGE_REFERENCE) && p1 == 0x00) {
CACKEY_DEBUG_PRINTF("Sending APDU: <<censored>>");
} else {
CACKEY_DEBUG_PRINTBUF("Sending APDU:", xmit_buf, xmit_len);
}
recv_len = sizeof(recv_buf);
memcpy(&pioRecvPci, pioSendPci, sizeof(pioRecvPci));
scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, &pioRecvPci, recv_buf, &recv_len);
CACKEY_DEBUG_PRINTF("SCardTransmit() completed with value: %s/%lx", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
if (scard_xmit_ret == SCARD_E_NOT_TRANSACTED) {
CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx), will ask calling function to retry (not resetting card)...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
/* End Smartcard Transaction */
cackey_end_transaction(slot);
cackey_reconnect_card(slot, slot->protocol);
return(CACKEY_PCSC_E_RETRY);
}
if (scard_xmit_ret == SCARD_E_NO_SERVICE) {
CACKEY_DEBUG_PRINTF("Failed to send APDU to card, possibly due to PC/SC handle being invalid (SCardTransmit() = %s/%lx), will ask calling function to retry...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
cackey_mark_slot_reset(slot);
return(CACKEY_PCSC_E_RETRY);
}
if (scard_xmit_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret);
CACKEY_DEBUG_PRINTF("Marking slot as having been reset");
cackey_mark_slot_reset(slot);
if (scard_xmit_ret == SCARD_W_RESET_CARD) {
CACKEY_DEBUG_PRINTF("Reset required, please hold...");
scard_reconn_ret = cackey_reconnect_card(slot, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1);
if (scard_reconn_ret == SCARD_S_SUCCESS) {
switch (slot->protocol) {
case SCARD_PROTOCOL_T0:
pioSendPci = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
pioSendPci = SCARD_PCI_T1;
break;
default:
CACKEY_DEBUG_PRINTF("Invalid protocol found, but too late to do anything about it now -- trying anyway.");
break;
}
/* Re-establish transaction, if it was present */
if (slot->transaction_depth > 0) {
slot->transaction_depth--;
slot->transaction_need_hw_lock = 1;
cackey_begin_transaction(slot);
}
CACKEY_DEBUG_PRINTF("Reset successful, retransmitting");
recv_len = sizeof(recv_buf);
memcpy(&pioRecvPci, pioSendPci, sizeof(pioRecvPci));
scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, &pioRecvPci, recv_buf, &recv_len);
if (scard_xmit_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Retransmit failed, returning in failure after disconnecting the card (SCardTransmit = %s/%li)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (long) scard_xmit_ret);
SCardDisconnect(slot->pcsc_card, SCARD_LEAVE_CARD);
slot->pcsc_card_connected = 0;
/* End Smartcard Transaction */
slot->transaction_depth = 1;
cackey_end_transaction(slot);
return(CACKEY_PCSC_E_TOKENABSENT);
}
} else {
CACKEY_DEBUG_PRINTF("Disconnecting card");
SCardDisconnect(slot->pcsc_card, SCARD_LEAVE_CARD);
slot->pcsc_card_connected = 0;
/* End Smartcard Transaction */
slot->transaction_depth = 1;
cackey_end_transaction(slot);
CACKEY_DEBUG_PRINTF("Returning in failure");
return(CACKEY_PCSC_E_TOKENABSENT);
}
} else {
CACKEY_DEBUG_PRINTF("Disconnecting card");
SCardDisconnect(slot->pcsc_card, SCARD_LEAVE_CARD);
slot->pcsc_card_connected = 0;
/* End Smartcard Transaction */
slot->transaction_depth = 1;
cackey_end_transaction(slot);
CACKEY_DEBUG_PRINTF("Returning in failure");
return(CACKEY_PCSC_E_TOKENABSENT);
}
}
CACKEY_DEBUG_PRINTBUF("Returned Value:", recv_buf, recv_len);
if (recv_len < 2) {
/* Minimal response length is 2 bytes, returning in failure */
CACKEY_DEBUG_PRINTF("Response too small, returning in failure (recv_len = %lu)", (unsigned long) recv_len);
/* End Smartcard Transaction */
cackey_end_transaction(slot);
/* Supply an invalid response code */
if (respcode) {
*respcode = 0;
}
return(CACKEY_PCSC_E_NODATA);
}
/* Determine result code */
major_rc = recv_buf[recv_len - 2];
minor_rc = recv_buf[recv_len - 1];
if (respcode) {
*respcode = (major_rc << 8) | minor_rc;
}
/* Adjust message buffer */
recv_len -= 2;
/* Add bytes to return value */
tmp_respdata_len = 0;
if (respdata && respdata_len) {
tmp_respdata_len = *respdata_len;
bytes_to_copy = *respdata_len;
if (recv_len < bytes_to_copy) {
bytes_to_copy = recv_len;
}
CACKEY_DEBUG_PRINTF("Copying %lu bytes to the buffer (recv'd %lu bytes, but only %lu bytes left in our buffer)", (unsigned long) bytes_to_copy, (unsigned long) recv_len, (unsigned long) *respdata_len);
memcpy(respdata, recv_buf, bytes_to_copy);
respdata += bytes_to_copy;
*respdata_len = bytes_to_copy;
tmp_respdata_len -= bytes_to_copy;
} else {
if (recv_len != 0) {
CACKEY_DEBUG_PRINTF("Throwing away %lu bytes, nowhere to put them!", (unsigned long) recv_len);
}
}
if (major_rc == 0x61) {
/* We need to READ */
CACKEY_DEBUG_PRINTF("Buffer read required");
if (minor_rc == 0x00) {
minor_rc = CACKEY_APDU_MTU;
}
pcsc_getresp_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_GET_RESPONSE, 0x00, 0x00, 0, NULL, minor_rc, respcode, respdata, &tmp_respdata_len);
if (pcsc_getresp_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Buffer read failed! Returning in failure");
/* End Smartcard Transaction */
cackey_end_transaction(slot);
if (pcsc_getresp_ret == CACKEY_PCSC_E_RETRY) {
return(CACKEY_PCSC_E_RETRY);
}
return(CACKEY_PCSC_E_GENERIC);
}
if (respdata_len) {
*respdata_len += tmp_respdata_len;
}
/* End Smartcard Transaction */
cackey_end_transaction(slot);
CACKEY_DEBUG_PRINTF("Returning in success (buffer read complete)");
return(CACKEY_PCSC_S_OK);
}
/* End Smartcard Transaction */
cackey_end_transaction(slot);
if (major_rc == 0x90) {
/* Success */
CACKEY_DEBUG_PRINTF("Returning in success (major_rc = 0x90)");
return(CACKEY_PCSC_S_OK);
}
CACKEY_DEBUG_PRINTF("APDU Returned an error, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
static unsigned char *cackey_read_bertlv_tag(unsigned char *buffer, size_t *buffer_len_p, unsigned char tag, unsigned char *outbuffer, size_t *outbuffer_len_p) {
unsigned char *buffer_p;
size_t outbuffer_len, buffer_len;
size_t size;
int idx;
CACKEY_DEBUG_PRINTF("Called.");
if (buffer_len_p == NULL) {
CACKEY_DEBUG_PRINTF("buffer_len_p is NULL. Returning in failure.");
return(NULL);
}
if (outbuffer_len_p == NULL) {
CACKEY_DEBUG_PRINTF("outbuffer_len_p is NULL. Returning in failure.");
return(NULL);
}
buffer_len = *outbuffer_len_p;
outbuffer_len = *outbuffer_len_p;
if (buffer_len < 2) {
CACKEY_DEBUG_PRINTF("buffer_len is less than 2, so we can't read any tag. Returning in failure.");
return(NULL);
}
buffer_p = buffer;
if (buffer_p[0] != tag) {
CACKEY_DEBUG_PRINTF("Tag found was not tag expected. Tag = %02x, Expected = %02x. Returning in failure.", (unsigned int) buffer_p[0], tag);
return(NULL);
}
buffer_p++;
buffer_len--;
if ((buffer_p[0] & 0x80) == 0x80) {
size = 0;
idx = (buffer_p[0] & 0x7f);
if (idx > buffer_len) {
CACKEY_DEBUG_PRINTF("Malformed BER value -- not enough bytes available to read length (idx = %i, buffer_len = %lu)", idx, (unsigned long) buffer_len);
return(NULL);
}
for (; idx > 0; idx--) {
buffer_p++;
buffer_len--;
size <<= 8;
size |= buffer_p[0];
}
} else {
size = buffer_p[0];
}
buffer_p++;
buffer_len--;
if (size > outbuffer_len) {
CACKEY_DEBUG_PRINTF("Unable to copy value buffer to outbuffer, not enough room. Value buffer length = %lu, out buffer length = %lu", (unsigned long) size, (unsigned long) outbuffer_len);
return(NULL);
}
*outbuffer_len_p = size;
if (outbuffer) {
memcpy(outbuffer, buffer_p, size);
buffer_p += size;
buffer_len -= size;
*buffer_len_p = buffer_len;
CACKEY_DEBUG_PRINTBUF("BER-TLV results:", outbuffer, size);
} else {
memmove(buffer, buffer_p, size);
buffer_p = buffer;
CACKEY_DEBUG_PRINTBUF("BER-TLV results:", buffer, size);
}
CACKEY_DEBUG_PRINTF("Returning in success. Size of contents for tag %02x is %lu", (unsigned int) tag, (unsigned long) size);
return(buffer_p);
}
/*
* SYNPOSIS
* ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t buffer_len, unsigned char oid[3]);
*
* ARGUMENTS
* struct cackey_slot *slot
* Slot to send commands to
*
* unsigned char *buffer
* [OUT] Buffer
*
* size_t buffer_len
* Number of bytes to attempt to read
*
* unsigned char oid[3]
* 3-byte OID to read
*
*
* RETURN VALUE
* This function returns the number of bytes actually read, or -1 on error.
*
* NOTES
* None
*
*/
static ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t buffer_len, unsigned char oid[3]) {
unsigned char cmd[] = {0x5C, 0x03, 0x00, 0x00, 0x00};
unsigned char *buffer_p;
size_t init_buffer_len, size;
uint16_t respcode;
int send_ret;
CACKEY_DEBUG_PRINTF("Called.");
init_buffer_len = buffer_len;
cmd[2] = oid[0];
cmd[3] = oid[1];
cmd[4] = oid[2];
/* 256 to indicate the largest message size -- not clear if this will work with all messages */
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, NISTSP800_73_3_INSTR_GET_DATA, 0x3F, 0xFF, sizeof(cmd), cmd, 256, &respcode, buffer, &buffer_len);
if (send_ret == CACKEY_PCSC_E_RETRY) {
CACKEY_DEBUG_PRINTF("ADPU Sending failed, retrying read buffer");
return(cackey_get_data(slot, buffer, init_buffer_len, oid));
}
if (send_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("cackey_send_apdu() failed, returning in failure");
return(-1);
}
#ifdef CACKEY_PARANOID
# ifdef _POSIX_SSIZE_MAX
if (buffer_len > _POSIX_SSIZE_MAX) {
CACKEY_DEBUG_PRINTF("Read bytes (buffer_len) exceeds maximum value, returning in failure. (max = %li, buffer_len = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) buffer_len);
return(-1);
}
# endif
#endif
if (buffer_len < 2) {
CACKEY_DEBUG_PRINTF("APDU GET DATA returned %lu bytes, which is too short for a BER-TLV response", (unsigned long) buffer_len);
return(-1);
}
size = buffer_len;
buffer_p = cackey_read_bertlv_tag(buffer, &buffer_len, 0x53, NULL, &size);
if (buffer_p == NULL) {
CACKEY_DEBUG_PRINTF("Tag decoding failed, returning in error.");
return(-1);
}
CACKEY_DEBUG_PRINTBUF("GET DATA result", buffer, size);
CACKEY_DEBUG_PRINTF("Returning in success, read %lu bytes", (unsigned long) size);
return(size);
}
/*
* SYNPOSIS
* ssize_t cackey_read_buffer(struct cackey_slot *slot, unsigned char *buffer, size_t count, unsigned char t_or_v, size_t initial_offset);
*
* ARGUMENTS
* struct cackey_slot *slot
* Slot to send commands to
*
* unsigned char *buffer
* [OUT] Buffer
*
* size_t count
* Number of bytes to attempt to read
*
* unsigned char t_or_v
* Select the T-buffer (01) or V-buffer (02) to read from.
*
* size_t initial_offset
* Specify the offset to begin the read from
*
*
* RETURN VALUE
* This function returns the number of bytes actually read, or -1 on error.
*
* NOTES
* None
*
*/
static ssize_t cackey_read_buffer(struct cackey_slot *slot, unsigned char *buffer, size_t count, unsigned char t_or_v, size_t initial_offset) {
unsigned char *init_buffer;
size_t init_count;
size_t init_initial_offset;
size_t offset = 0, max_offset, max_count;
unsigned char cmd[2];
uint16_t respcode;
int send_ret;
CACKEY_DEBUG_PRINTF("Called.");
init_buffer = buffer;
init_count = count;
init_initial_offset = initial_offset;
max_offset = count;
max_count = CACKEY_APDU_MTU;
if (t_or_v != 1 && t_or_v != 2) {
CACKEY_DEBUG_PRINTF("Invalid T or V parameter specified, returning in failure");
return(-1);
}
cmd[0] = t_or_v;
while (1) {
if (offset >= max_offset) {
CACKEY_DEBUG_PRINTF("Buffer too small, returning what we got...");
break;
}
count = max_offset - offset;
if (count > max_count) {
count = max_count;
}
cmd[1] = count;
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_GLOBAL_PLATFORM, GSCIS_INSTR_READ_BUFFER, ((initial_offset + offset) >> 8) & 0xff, (initial_offset + offset) & 0xff, sizeof(cmd), cmd, 0x00, &respcode, buffer + offset, &count);
if (send_ret == CACKEY_PCSC_E_RETRY) {
CACKEY_DEBUG_PRINTF("ADPU Sending failed, retrying read buffer");
return(cackey_read_buffer(slot, init_buffer, init_count, t_or_v, init_initial_offset));
}
if (send_ret != CACKEY_PCSC_S_OK) {
if (respcode == 0x6A86) {
if (max_count == 1) {
break;
}
max_count = max_count / 2;
continue;
}
CACKEY_DEBUG_PRINTF("cackey_send_apdu() failed, returning in failure");
return(-1);
}
offset += count;
if (count < max_count) {
CACKEY_DEBUG_PRINTF("Short read -- count = %i, cmd[1] = %i", (int) count, (int) cmd[1]);
break;
}
}
#ifdef CACKEY_PARANOID
# ifdef _POSIX_SSIZE_MAX
if (offset > _POSIX_SSIZE_MAX) {
CACKEY_DEBUG_PRINTF("Offset exceeds maximum value, returning in failure. (max = %li, offset = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) offset);
return(-1);
}
# endif
#endif
CACKEY_DEBUG_PRINTF("Returning in success, read %lu bytes", (unsigned long) offset);
return(offset);
}
/*
* SYNPOSIS
* cackey_ret cackey_select_applet(struct cackey_slot *slot, unsigned char *aid, size_t aid_len);
*
* ARGUMENTS
* struct cackey_slot *slot
* Slot to send commands to
*
* unsigned char *aid
* Buffer containing Applet ID to select
*
* size_t aid_len
* Number of bytes in the "aid" (Applet ID) parameter
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* None
*
*/
static cackey_ret cackey_select_applet(struct cackey_slot *slot, unsigned char *aid, size_t aid_len) {
int send_ret;
CACKEY_DEBUG_PRINTF("Called.");
CACKEY_DEBUG_PRINTBUF("Selecting applet:", aid, aid_len);
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_SELECT, GSCIS_PARAM_SELECT_APPLET, 0x00, aid_len, aid, 0x00, NULL, NULL, NULL);
if (send_ret == CACKEY_PCSC_E_RETRY) {
CACKEY_DEBUG_PRINTF("ADPU Sending failed, retrying select applet");
return(cackey_select_applet(slot, aid, aid_len));
}
if (send_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Failed to open applet, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("Successfully selected file");
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint);
*
* ARGUMENTS
* struct cackey_slot *slot
* Slot to send commands to
*
* cackey_pcsc_id_type type_hint
* A hint as to which type of card might be in this slot (CAC or PIV)
*
* RETURN VALUE
* CACKEY_ID_TYPE_PIV If the card connected is a PIV
* CACKEY_ID_TYPE_CAC If the card connected is a CAC with the CCC
* applet
* CACKEY_ID_TYPE_ERROR If we are unable to determine what type of card
* is connected
*
* NOTES
* This function reselects the "root" applet, after this function is called
* the user may be required to login again
*
*/
static cackey_pcsc_id_type cackey_detect_and_select_root_applet(struct cackey_slot *slot, cackey_pcsc_id_type type_hint) {
unsigned char cac_ccc_aid[] = {GSCIS_AID_CCC};
unsigned char cac_id0_aid[] = {GSCIS_AID_ID0};
unsigned char piv_aid[] = {NISTSP800_73_3_PIV_AID};
cackey_pcsc_id_type try_types[2], try_type;
int send_ret;
int idx;
CACKEY_DEBUG_PRINTF("Reselecting the root applet");
if (type_hint == CACKEY_ID_TYPE_UNKNOWN) {
type_hint = slot->id_type_hint;
}
slot->id_type_hint = CACKEY_ID_TYPE_UNKNOWN;
switch (type_hint) {
case CACKEY_ID_TYPE_PIV:
CACKEY_DEBUG_PRINTF("Trying to reselect the PIV root applet first");
try_types[0] = CACKEY_ID_TYPE_PIV;
try_types[1] = CACKEY_ID_TYPE_CAC;
break;
default:
CACKEY_DEBUG_PRINTF("Trying to reselect the CAC CCC applet first");
try_types[0] = CACKEY_ID_TYPE_CAC;
try_types[1] = CACKEY_ID_TYPE_PIV;
break;
}
for (idx = 0; idx < (sizeof(try_types) / sizeof(try_types[0])); idx++) {
try_type = try_types[idx];
switch (try_type) {
case CACKEY_ID_TYPE_CAC:
CACKEY_DEBUG_PRINTF("Trying to select the CAC CCC applet");
send_ret = cackey_select_applet(slot, cac_ccc_aid, sizeof(cac_ccc_aid));
if (send_ret != CACKEY_PCSC_S_OK) {
send_ret = cackey_select_applet(slot, cac_id0_aid, sizeof(cac_id0_aid));
}
break;
case CACKEY_ID_TYPE_PIV:
CACKEY_DEBUG_PRINTF("Trying to select the PIV root applet");
send_ret = cackey_select_applet(slot, piv_aid, sizeof(piv_aid));
break;
}
if (send_ret == CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Successfully selected the %s applet -- setting the \"LOGIN REQUIRED\" flag on the token",
try_type == CACKEY_ID_TYPE_CAC ? "CAC" : "PIV"
);
slot->token_flags = CKF_LOGIN_REQUIRED;
slot->id_type_hint = try_type;
return(try_type);
}
}
CACKEY_DEBUG_PRINTF("Unable to select any applet, returning in failure");
return(CACKEY_ID_TYPE_ERROR);
}
/*
* SYNPOSIS
* cackey_ret cackey_select_file(struct cackey_slot *slot, uint16_t ef);
*
* ARGUMENTS
* struct cackey_slot *slot
* Slot to send commands to
*
* uint16_t ef
* Elemental File to select
*
* RETURN VALUE
* CACKEY_PCSC_S_OK On success
* CACKEY_PCSC_E_GENERIC On error
*
* NOTES
* This selects an Elementary File (EF) under the currently selected
* Dedicated File (DF)
*
* Typically this is called after selecting the correct Applet (using
* cackey_select_applet) for VM cards
*
*/
static cackey_ret cackey_select_file(struct cackey_slot *slot, uint16_t ef) {
unsigned char fid_buf[2];
int send_ret;
CACKEY_DEBUG_PRINTF("Called.");
/* Open the elementary file */
fid_buf[0] = (ef >> 8) & 0xff;
fid_buf[1] = ef & 0xff;
CACKEY_DEBUG_PRINTF("Selecting file: %04lx", (unsigned long) ef);
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_SELECT, 0x02, 0x0C, sizeof(fid_buf), fid_buf, 0x00, NULL, NULL, NULL);
if (send_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Failed to open file, returning in failure");
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("Successfully selected file");
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* void cackey_free_tlv(struct cackey_tlv_entity *root);
*
* ARGUMENTS
* struct cackey_tlv_entity *root
* Root of the TLV list to start freeing
*
* RETURN VALUE
* None
*
* NOTES
* This function frees the TLV linked listed returned from
* "cackey_read_tlv"
*
*/
static void cackey_free_tlv(struct cackey_tlv_entity *root) {
struct cackey_tlv_entity *curr, *next;
if (root == NULL) {
return;
}
for (curr = root; curr; curr = next) {
next = curr->_next;
switch (curr->tag) {
case GSCIS_TAG_ACR_TABLE:
case GSCIS_TAG_CERTIFICATE:
if (curr->value) {
free(curr->value);
}
break;
case GSCIS_TAG_CARDURL:
if (curr->value_cardurl) {
free(curr->value_cardurl);
}
break;
}
free(curr);
}
return;
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static struct cackey_tlv_entity *cackey_read_tlv(struct cackey_slot *slot) {
struct cackey_tlv_entity *curr_entity, *root = NULL, *last = NULL;
unsigned char tlen_buf[2], tval_buf[1024], *tval;
unsigned char vlen_buf[2], vval_buf[8192], *vval;
unsigned char *tmpbuf;
unsigned long tmpbuflen;
ssize_t tlen, vlen;
ssize_t read_ret;
size_t offset_t = 0, offset_v = 0;
unsigned char tag;
size_t length;
#ifdef HAVE_LIBZ
int uncompress_ret;
#endif
CACKEY_DEBUG_PRINTF("Called.");
read_ret = cackey_read_buffer(slot, tlen_buf, sizeof(tlen_buf), 1, offset_t);
if (read_ret != sizeof(tlen_buf)) {
CACKEY_DEBUG_PRINTF("Read failed, returning in failure");
return(NULL);
}
tlen = (tlen_buf[1] << 8) | tlen_buf[0];
read_ret = cackey_read_buffer(slot, vlen_buf, sizeof(vlen_buf), 2, offset_v);
if (read_ret != sizeof(vlen_buf)) {
CACKEY_DEBUG_PRINTF("Read failed, returning in failure");
return(NULL);
}
vlen = (vlen_buf[1] << 8) | vlen_buf[0];
CACKEY_DEBUG_PRINTF("Tag Length = %lu, Value Length = %lu", (unsigned long) tlen, (unsigned long) vlen);
offset_t += 2;
offset_v += 2;
if (tlen > sizeof(tval_buf)) {
CACKEY_DEBUG_PRINTF("Tag length is too large, returning in failure");
return(NULL);
}
if (vlen > sizeof(vval_buf)) {
CACKEY_DEBUG_PRINTF("Value length is too large, returning in failure");
return(NULL);
}
read_ret = cackey_read_buffer(slot, tval_buf, tlen, 1, offset_t);
if (read_ret != tlen) {
CACKEY_DEBUG_PRINTF("Unable to read entire T-buffer, returning in failure");
return(NULL);
}
read_ret = cackey_read_buffer(slot, vval_buf, vlen, 2, offset_v);
if (read_ret != vlen) {
CACKEY_DEBUG_PRINTF("Unable to read entire V-buffer, returning in failure");
return(NULL);
}
tval = tval_buf;
vval = vval_buf;
while (tlen > 0 && vlen > 0) {
tag = *tval;
tval++;
tlen--;
if (*tval == 0xff) {
length = (tval[2] << 8) | tval[1];
tval += 3;
tlen -= 3;
} else {
length = *tval;
tval++;
tlen--;
}
CACKEY_DEBUG_PRINTF("Tag: %s (%02x)", CACKEY_DEBUG_FUNC_TAG_TO_STR(tag), (unsigned int) tag);
CACKEY_DEBUG_PRINTBUF("Value:", vval, length);
curr_entity = NULL;
switch (tag) {
case GSCIS_TAG_CARDURL:
curr_entity = malloc(sizeof(*curr_entity));
curr_entity->value_cardurl = malloc(sizeof(*curr_entity->value_cardurl));
memcpy(curr_entity->value_cardurl->rid, vval, 5);
curr_entity->value_cardurl->apptype = vval[5];
curr_entity->value_cardurl->objectid = (vval[6] << 8) | vval[7];
curr_entity->value_cardurl->appid = (vval[8] << 8) | vval[9];
curr_entity->tag = tag;
curr_entity->_next = NULL;
break;
case GSCIS_TAG_ACR_TABLE:
curr_entity = malloc(sizeof(*curr_entity));
tmpbuf = malloc(length);
memcpy(tmpbuf, vval, length);
curr_entity->tag = tag;
curr_entity->length = length;
curr_entity->value = tmpbuf;
curr_entity->_next = NULL;
break;
case GSCIS_TAG_CERTIFICATE:
curr_entity = malloc(sizeof(*curr_entity));
#ifdef HAVE_LIBZ
tmpbuflen = length * 2;
tmpbuf = malloc(tmpbuflen);
uncompress_ret = uncompress(tmpbuf, &tmpbuflen, vval, length);
if (uncompress_ret != Z_OK) {
CACKEY_DEBUG_PRINTF("Failed to decompress, uncompress() returned %i -- resorting to direct copy", uncompress_ret);
tmpbuflen = length;
memcpy(tmpbuf, vval, length);
}
CACKEY_DEBUG_PRINTBUF("Decompressed to:", tmpbuf, tmpbuflen);
#else
CACKEY_DEBUG_PRINTF("Missing ZLIB Support, this certificate is likely useless...");
tmpbuflen = length;
memcpy(tmpbuf, vval, length);
#endif
curr_entity->tag = tag;
curr_entity->length = tmpbuflen;
curr_entity->value = tmpbuf;
curr_entity->_next = NULL;
break;
case GSCIS_TAG_PKCS15:
curr_entity = malloc(sizeof(*curr_entity));
curr_entity->tag = tag;
curr_entity->value_byte = vval[0];
curr_entity->_next = NULL;
break;
}
vval += length;
vlen -= length;
if (curr_entity != NULL) {
if (root == NULL) {
root = curr_entity;
}
if (last != NULL) {
last->_next = curr_entity;
}
last = curr_entity;
}
}
return(root);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static void cackey_free_certs(struct cackey_pcsc_identity *start, size_t count, int free_start) {
size_t idx;
if (start == NULL) {
return;
}
for (idx = 0; idx < count; idx++) {
if (start[idx].certificate) {
free(start[idx].certificate);
}
}
if (free_start) {
free(start);
}
return;
}
static struct cackey_pcsc_identity *cackey_copy_certs(struct cackey_pcsc_identity *dest, struct cackey_pcsc_identity *start, size_t count) {
size_t idx;
if (start == NULL) {
return(NULL);
}
if (dest == NULL) {
dest = malloc(sizeof(*dest) * count);
}
for (idx = 0; idx < count; idx++) {
dest[idx].id_type = start[idx].id_type;
switch (dest[idx].id_type) {
case CACKEY_ID_TYPE_CAC:
memcpy(dest[idx].card.cac.applet, start[idx].card.cac.applet, sizeof(dest[idx].card.cac.applet));
dest[idx].card.cac.file = start[idx].card.cac.file;
break;
case CACKEY_ID_TYPE_PIV:
dest[idx].card.piv.key_id = start[idx].card.piv.key_id;
memcpy(dest[idx].card.piv.label, start[idx].card.piv.label, sizeof(dest[idx].card.piv.label));
break;
case CACKEY_ID_TYPE_CERT_ONLY:
break;
}
dest[idx].certificate_len = start[idx].certificate_len;
dest[idx].keysize = start[idx].keysize;
dest[idx].certificate = malloc(dest[idx].certificate_len);
memcpy(dest[idx].certificate, start[idx].certificate, dest[idx].certificate_len);
}
return(dest);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static struct cackey_pcsc_identity *cackey_read_certs(struct cackey_slot *slot, struct cackey_pcsc_identity *certs, unsigned long *count) {
cackey_pcsc_id_type check_id_type;
struct cackey_pcsc_identity *curr_id;
struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr;
unsigned char *piv_oid, piv_oid_pivauth[] = {NISTSP800_73_3_OID_PIVAUTH}, piv_oid_signature[] = {NISTSP800_73_3_OID_SIGNATURE}, piv_oid_keymgt[] = {NISTSP800_73_3_OID_KEYMGT};
unsigned char curr_aid[7];
unsigned char buffer[8192], *buffer_p, *tmpbuf;
unsigned long outidx = 0;
char *piv_label;
cackey_ret transaction_ret;
ssize_t read_ret;
size_t buffer_len, tmpbuflen;
int certs_resizable;
int send_ret, select_ret;
int piv_key, piv = 0;
int cached_certs_valid;
int idx;
cackey_pcsc_id_type id_type;
#ifdef HAVE_LIBZ
int uncompress_ret;
z_stream gzip_stream;
#endif
CACKEY_DEBUG_PRINTF("Called.");
if (count == NULL) {
CACKEY_DEBUG_PRINTF("count is NULL, returning in failure");
return(NULL);
}
if (certs != NULL) {
if (*count == 0) {
CACKEY_DEBUG_PRINTF("Requested we return 0 objects, short-circuit");
return(certs);
}
}
cached_certs_valid = 0;
if (!slot->slot_reset) {
if (slot->cached_certs) {
cached_certs_valid = 1;
if (slot->cached_certs_count > 0) {
cached_certs_valid = 1;
}
}
}
if (cached_certs_valid) {
if (certs == NULL) {
certs = malloc(sizeof(*certs) * slot->cached_certs_count);
*count = slot->cached_certs_count;
} else {
if (*count > slot->cached_certs_count) {
*count = slot->cached_certs_count;
}
}
cackey_copy_certs(certs, slot->cached_certs, *count);
CACKEY_DEBUG_PRINTF("Returning cached certificates for this slot (card has not been reset and there are cached certs available)");
return(certs);
}
if (slot->cached_certs) {
cackey_free_certs(slot->cached_certs, slot->cached_certs_count, 1);
slot->cached_certs = NULL;
}
/* Begin a SmartCard transaction */
transaction_ret = cackey_begin_transaction(slot);
if (transaction_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable begin transaction, returning in failure");
return(NULL);
}
id_type = cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);
switch (id_type) {
case CACKEY_ID_TYPE_CAC:
piv = 0;
break;
case CACKEY_ID_TYPE_PIV:
piv = 1;
break;
case CACKEY_ID_TYPE_ERROR:
/* Terminate SmartCard Transaction */
cackey_end_transaction(slot);
if (certs == NULL) {
*count = 0;
}
return(NULL);
break;
case CACKEY_ID_TYPE_CERT_ONLY:
CACKEY_DEBUG_PRINTF("Error. Impossible condition reached.");
break;
}
if (certs == NULL) {
certs = malloc(sizeof(*certs) * 5);
*count = 5;
certs_resizable = 1;
} else {
certs_resizable = 0;
}
if (piv) {
for (idx = 0; idx < 3; idx++) {
switch (idx) {
case 0:
piv_oid = piv_oid_pivauth;
piv_key = NISTSP800_78_3_KEY_PIVAUTH;
piv_label = "Authentication";
break;
case 1:
piv_oid = piv_oid_signature;
piv_key = NISTSP800_78_3_KEY_SIGNATURE;
piv_label = "Signature";
break;
case 2:
piv_oid = piv_oid_keymgt;
piv_key = NISTSP800_78_3_KEY_KEYMGT;
piv_label = "Key Management";
break;
}
read_ret = cackey_get_data(slot, buffer, sizeof(buffer), piv_oid);
if (read_ret <= 0) {
continue;
}
curr_id = &certs[outidx];
outidx++;
curr_id->keysize = -1;
curr_id->id_type = CACKEY_ID_TYPE_PIV;
curr_id->card.piv.key_id = piv_key;
memcpy(curr_id->card.piv.label, piv_label, strlen(piv_label) + 1);
curr_id->certificate_len = read_ret;
curr_id->certificate = malloc(curr_id->certificate_len);
buffer_len = sizeof(buffer);
buffer_p = cackey_read_bertlv_tag(buffer, &buffer_len, 0x70, curr_id->certificate, &curr_id->certificate_len);
if (buffer_p == NULL) {
CACKEY_DEBUG_PRINTF("Reading certificate from BER-TLV response failed, skipping key %i", idx);
free(curr_id->certificate);
curr_id->certificate = NULL;
outidx--;
continue;
}
if (curr_id->certificate_len > 4) {
if (memcmp(curr_id->certificate, "\x1f\x8b\x08\x00", 4) == 0) {
#ifdef HAVE_LIBZ
tmpbuflen = curr_id->certificate_len * 2;
tmpbuf = malloc(tmpbuflen);
CACKEY_DEBUG_PRINTBUF("Attempting to decompress:", curr_id->certificate, curr_id->certificate_len);
gzip_stream.zalloc = NULL;
gzip_stream.zfree = NULL;
gzip_stream.opaque = NULL;
gzip_stream.next_in = curr_id->certificate;
gzip_stream.avail_in = curr_id->certificate_len;
gzip_stream.next_out = tmpbuf;
gzip_stream.avail_out = tmpbuflen;
uncompress_ret = inflateInit(&gzip_stream);
if (uncompress_ret == Z_OK) {
/* Try again as a gzip buffer */
uncompress_ret = inflateEnd(&gzip_stream);
if (uncompress_ret == Z_OK) {
gzip_stream.zalloc = NULL;
gzip_stream.zfree = NULL;
gzip_stream.opaque = NULL;
gzip_stream.next_in = curr_id->certificate;
gzip_stream.avail_in = curr_id->certificate_len;
gzip_stream.next_out = tmpbuf;
gzip_stream.avail_out = tmpbuflen;
uncompress_ret = inflateInit2(&gzip_stream, 31);
}
}
if (uncompress_ret == Z_OK) {
uncompress_ret = inflate(&gzip_stream, 0);
}
if (uncompress_ret == Z_STREAM_END) {
uncompress_ret = inflateEnd(&gzip_stream);
} else {
uncompress_ret = Z_DATA_ERROR;
}
if (uncompress_ret == Z_OK) {
tmpbuflen = gzip_stream.total_out;
CACKEY_DEBUG_PRINTBUF("Decompressed to:", tmpbuf, tmpbuflen);
free(curr_id->certificate);
curr_id->certificate = tmpbuf;
curr_id->certificate_len = tmpbuflen;
} else {
CACKEY_DEBUG_PRINTF("Decompressing failed! uncompress() returned %i", uncompress_ret);
free(tmpbuf);
}
#else
CACKEY_DEBUG_PRINTF("Error. We got a compressed certificate but we do not have zlib. Hoping for the best.");
#endif
}
}
}
} else {
/* Read all the applets from the CCC's TLV */
ccc_tlv = cackey_read_tlv(slot);
/* Look for CARDURLs that coorespond to PKI applets */
for (ccc_curr = ccc_tlv; ccc_curr; ccc_curr = ccc_curr->_next) {
CACKEY_DEBUG_PRINTF("Found tag: %s ... ", CACKEY_DEBUG_FUNC_TAG_TO_STR(ccc_curr->tag));
if (ccc_curr->tag != GSCIS_TAG_CARDURL) {
CACKEY_DEBUG_PRINTF(" ... skipping it (we only care about CARDURLs)");
continue;
}
if ((ccc_curr->value_cardurl->apptype & CACKEY_TLV_APP_PKI) != CACKEY_TLV_APP_PKI) {
CACKEY_DEBUG_PRINTF(" ... skipping it (we only care about PKI applets, this applet supports: %s/%02x)", CACKEY_DEBUG_FUNC_APPTYPE_TO_STR(ccc_curr->value_cardurl->apptype), (unsigned int) ccc_curr->value_cardurl->apptype);
continue;
}
CACKEY_DEBUG_PRINTBUF("RID:", ccc_curr->value_cardurl->rid, sizeof(ccc_curr->value_cardurl->rid));
CACKEY_DEBUG_PRINTF("AppID = %s/%04lx", CACKEY_DEBUG_FUNC_OBJID_TO_STR(ccc_curr->value_cardurl->appid), (unsigned long) ccc_curr->value_cardurl->appid);
CACKEY_DEBUG_PRINTF("ObjectID = %s/%04lx", CACKEY_DEBUG_FUNC_OBJID_TO_STR(ccc_curr->value_cardurl->objectid), (unsigned long) ccc_curr->value_cardurl->objectid);
memcpy(curr_aid, ccc_curr->value_cardurl->rid, sizeof(ccc_curr->value_cardurl->rid));
curr_aid[sizeof(curr_aid) - 2] = (ccc_curr->value_cardurl->appid >> 8) & 0xff;
curr_aid[sizeof(curr_aid) - 1] = ccc_curr->value_cardurl->appid & 0xff;
/* Select found applet ... */
select_ret = cackey_select_applet(slot, curr_aid, sizeof(curr_aid));
if (select_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Failed to select applet, skipping processing of this object");
continue;
}
/* ... and object (file) */
select_ret = cackey_select_file(slot, ccc_curr->value_cardurl->objectid);
if (select_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Failed to select file, skipping processing of this object");
continue;
}
/* Process this file's TLV looking for certificates */
app_tlv = cackey_read_tlv(slot);
for (app_curr = app_tlv; app_curr; app_curr = app_curr->_next) {
CACKEY_DEBUG_PRINTF("Found tag: %s", CACKEY_DEBUG_FUNC_TAG_TO_STR(app_curr->tag));
if (app_curr->tag != GSCIS_TAG_CERTIFICATE) {
CACKEY_DEBUG_PRINTF(" ... skipping it (we only care about CERTIFICATEs)");
continue;
}
curr_id = &certs[outidx];
outidx++;
curr_id->id_type = CACKEY_ID_TYPE_CAC;
memcpy(curr_id->card.cac.applet, curr_aid, sizeof(curr_id->card.cac.applet));
curr_id->card.cac.file = ccc_curr->value_cardurl->objectid;
curr_id->keysize = -1;
CACKEY_DEBUG_PRINTF("Filling curr_id->card.cac.applet (%p) with %lu bytes:", curr_id->card.cac.applet, (unsigned long) sizeof(curr_id->card.cac.applet));
CACKEY_DEBUG_PRINTBUF("VAL:", curr_id->card.cac.applet, sizeof(curr_id->card.cac.applet));
curr_id->certificate_len = app_curr->length;
curr_id->certificate = malloc(curr_id->certificate_len);
memcpy(curr_id->certificate, app_curr->value, curr_id->certificate_len);
if (outidx >= *count) {
if (certs_resizable) {
*count *= 2;
if (*count != 0) {
certs = realloc(certs, sizeof(*certs) * (*count));
} else {
certs = NULL;
}
} else {
break;
}
}
}
cackey_free_tlv(app_tlv);
if (outidx >= *count) {
break;
}
}
cackey_free_tlv(ccc_tlv);
}
*count = outidx;
if (certs_resizable) {
if (*count != 0) {
certs = realloc(certs, sizeof(*certs) * (*count));
} else {
free(certs);
certs = NULL;
}
}
slot->cached_certs = cackey_copy_certs(NULL, certs, *count);
slot->cached_certs_count = *count;
/* Terminate SmartCard Transaction */
cackey_end_transaction(slot);
return(certs);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static ssize_t cackey_signdecrypt(struct cackey_slot *slot, struct cackey_identity *identity, unsigned char *buf, size_t buflen, unsigned char *outbuf, size_t outbuflen, int padInput, int unpadOutput) {
cackey_pcsc_id_type id_type, check_id_type;
unsigned char dyn_auth_template[10], *dyn_auth_tmpbuf;
unsigned char *tmpbuf, *tmpbuf_s, *outbuf_s, *outbuf_p;
unsigned char bytes_to_send, p1, class;
unsigned char blocktype;
cackey_ret send_ret;
uint16_t respcode;
ssize_t retval = 0, unpadoffset;
size_t tmpbuflen, padlen, tmpoutbuflen, outbuf_len;
int free_tmpbuf = 0;
int le;
CACKEY_DEBUG_PRINTF("Called.");
if (slot == NULL) {
CACKEY_DEBUG_PRINTF("Error. slot is NULL");
return(-1);
}
if (buf == NULL) {
CACKEY_DEBUG_PRINTF("Error. buf is NULL");
return(-1);
}
if (outbuf == NULL) {
CACKEY_DEBUG_PRINTF("Error. outbuf is NULL");
return(-1);
}
if (identity == NULL) {
CACKEY_DEBUG_PRINTF("Error. identity is NULL");
return(-1);
}
if (identity->pcsc_identity == NULL) {
CACKEY_DEBUG_PRINTF("Error. identity->pcsc_identity is NULL");
return(-1);
}
id_type = identity->pcsc_identity->id_type;
if (id_type == CACKEY_ID_TYPE_CERT_ONLY) {
CACKEY_DEBUG_PRINTF("Error. identity->pcsc_identity is CACKEY_ID_TYPE_CERT_ONLY, which cannot be used for sign/decrypt");
return(-1);
}
switch (id_type) {
case CACKEY_ID_TYPE_PIV:
case CACKEY_ID_TYPE_CAC:
break;
default:
CACKEY_DEBUG_PRINTF("Error. identity->pcsc_identity is not a supported value. Type is: 0x%lx (PIV = 0x%lx, CAC = 0x%lx)", (unsigned long) id_type, (unsigned long) CACKEY_ID_TYPE_PIV, (unsigned long) CACKEY_ID_TYPE_CAC);
return(-1);
}
/* Determine identity Key size */
if (identity->pcsc_identity->keysize < 0) {
identity->pcsc_identity->keysize = x509_to_keysize(identity->pcsc_identity->certificate, identity->pcsc_identity->certificate_len);
}
/* Pad message to key size */
if (padInput) {
if (identity->pcsc_identity->keysize > 0) {
if (buflen != identity->pcsc_identity->keysize) {
if (buflen > (identity->pcsc_identity->keysize - 3)) {
CACKEY_DEBUG_PRINTF("Error. Message is too large to sign/decrypt");
return(-1);
}
tmpbuflen = identity->pcsc_identity->keysize;
tmpbuf = malloc(tmpbuflen);
free_tmpbuf = 1;
padlen = tmpbuflen - buflen - 3;
CACKEY_DEBUG_PRINTF("Need to pad the buffer with %llu bytes (tmpbuflen = %llu, buflen = %llu)", (unsigned long long) padlen, (unsigned long long) tmpbuflen, (unsigned long long) buflen);
/* RSA PKCS#1 EMSA-PKCS1-v1_5 Padding */
tmpbuf[0] = 0x00;
tmpbuf[1] = 0x01;
memset(&tmpbuf[2], 0xFF, padlen);
tmpbuf[padlen + 2]= 0x00;
memcpy(&tmpbuf[padlen + 3], buf, buflen);
CACKEY_DEBUG_PRINTBUF("Unpadded:", buf, buflen);
CACKEY_DEBUG_PRINTBUF("Padded:", tmpbuf, tmpbuflen);
} else {
tmpbuf = buf;
tmpbuflen = buflen;
free_tmpbuf = 0;
padlen = 0;
}
} else {
CACKEY_DEBUG_PRINTF("Unable to determine key size, hoping the message is properly padded!");
tmpbuf = buf;
tmpbuflen = buflen;
free_tmpbuf = 0;
padlen = 0;
}
} else {
tmpbuf = buf;
tmpbuflen = buflen;
free_tmpbuf = 0;
padlen = 0;
}
/* Begin transaction */
cackey_begin_transaction(slot);
/* Select correct applet */
switch (id_type) {
case CACKEY_ID_TYPE_CAC:
CACKEY_DEBUG_PRINTF("Selecting applet found at %p ...", identity->pcsc_identity->card.cac.applet);
cackey_select_applet(slot, identity->pcsc_identity->card.cac.applet, sizeof(identity->pcsc_identity->card.cac.applet));
/* Select correct file */
cackey_select_file(slot, identity->pcsc_identity->card.cac.file);
break;
case CACKEY_ID_TYPE_PIV:
dyn_auth_template[0] = 0x7C;
dyn_auth_template[1] = 0x82;
dyn_auth_template[2] = ((tmpbuflen + 6) & 0xff00) >> 8;
dyn_auth_template[3] = (tmpbuflen + 6) & 0x00ff;
dyn_auth_template[4] = 0x82;
dyn_auth_template[5] = 0x00;
dyn_auth_template[6] = 0x81;
dyn_auth_template[7] = 0x82;
dyn_auth_template[8] = (tmpbuflen & 0xff00) >> 8;
dyn_auth_template[9] = tmpbuflen & 0x00ff;
dyn_auth_tmpbuf = malloc(tmpbuflen + sizeof(dyn_auth_template));
memcpy(dyn_auth_tmpbuf, dyn_auth_template, sizeof(dyn_auth_template));
memcpy(dyn_auth_tmpbuf + sizeof(dyn_auth_template), tmpbuf, tmpbuflen);
if (free_tmpbuf) {
free(tmpbuf);
}
tmpbuflen += sizeof(dyn_auth_template);
tmpbuf = dyn_auth_tmpbuf;
free_tmpbuf = 1;
break;
case CACKEY_ID_TYPE_CERT_ONLY:
break;
}
tmpbuf_s = tmpbuf;
outbuf_s = outbuf;
while (tmpbuflen) {
tmpoutbuflen = outbuflen;
if (tmpbuflen > CACKEY_APDU_MTU) {
bytes_to_send = CACKEY_APDU_MTU;
} else {
bytes_to_send = tmpbuflen;
}
send_ret = CACKEY_PCSC_E_GENERIC;
switch (id_type) {
case CACKEY_ID_TYPE_CAC:
if (tmpbuflen > CACKEY_APDU_MTU) {
p1 = 0x80;
le = 0x00;
} else {
p1 = 0x00;
le = 0x00;
}
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_GLOBAL_PLATFORM, GSCIS_INSTR_SIGNDECRYPT, p1, 0x00, bytes_to_send, tmpbuf, le, &respcode, outbuf, &tmpoutbuflen);
break;
case CACKEY_ID_TYPE_PIV:
if (tmpbuflen > CACKEY_APDU_MTU) {
class = 0x10;
le = 0x00;
} else {
class = GSCIS_CLASS_ISO7816;
le = 256;
}
send_ret = cackey_send_apdu(slot, class, NISTSP800_73_3_INSTR_GENAUTH, NISTSP800_78_3_ALGO_RSA2048, identity->pcsc_identity->card.piv.key_id, bytes_to_send, tmpbuf, le, &respcode, outbuf, &tmpoutbuflen);
break;
case CACKEY_ID_TYPE_CERT_ONLY:
break;
}
if (send_ret != CACKEY_PCSC_S_OK) {
if (free_tmpbuf) {
if (tmpbuf_s) {
free(tmpbuf_s);
}
}
/* End transaction */
cackey_end_transaction(slot);
if (send_ret == CACKEY_PCSC_E_RETRY) {
CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- retrying.");
return(cackey_signdecrypt(slot, identity, buf, buflen, outbuf, outbuflen, padInput, unpadOutput));
}
CACKEY_DEBUG_PRINTF("ADPU Sending Failed -- returning in error.");
if (respcode == 0x6982 || respcode == 0x6e00 || respcode == 0x6d00) {
if (respcode == 0x6e00) {
CACKEY_DEBUG_PRINTF("Got \"WRONG CLASS\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
} else if (respcode == 0x6d00) {
CACKEY_DEBUG_PRINTF("Got \"INVALID INSTRUCTION\", this means we are talking to the wrong object (likely because the card went away) -- resetting");
} else {
CACKEY_DEBUG_PRINTF("Security status not satisified (respcode = 0x%04x). Returning NEEDLOGIN", (int) respcode);
}
cackey_mark_slot_reset(slot);
cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);
slot->token_flags = CKF_LOGIN_REQUIRED;
return(CACKEY_PCSC_E_NEEDLOGIN);
}
if (send_ret == CACKEY_PCSC_E_TOKENABSENT) {
CACKEY_DEBUG_PRINTF("Token absent. Returning TOKENABSENT");
cackey_mark_slot_reset(slot);
return(CACKEY_PCSC_E_TOKENABSENT);
}
CACKEY_DEBUG_PRINTF("Something went wrong during signing, resetting the slot and hoping for the best.");
cackey_pcsc_disconnect();
cackey_pcsc_connect();
cackey_detect_and_select_root_applet(slot, CACKEY_ID_TYPE_UNKNOWN);
return(CACKEY_PCSC_E_GENERIC);
}
tmpbuf += bytes_to_send;
tmpbuflen -= bytes_to_send;
outbuf += tmpoutbuflen;
outbuflen -= tmpoutbuflen;
retval += tmpoutbuflen;
}
if (free_tmpbuf) {
if (tmpbuf_s) {
free(tmpbuf_s);
}
}
outbuf = outbuf_s;
/* End transaction */
cackey_end_transaction(slot);
#ifdef CACKEY_PARANOID
# ifdef _POSIX_SSIZE_MAX
if (outbuflen > _POSIX_SSIZE_MAX) {
CACKEY_DEBUG_PRINTF("Outbuflen exceeds maximum value, returning in failure. (max = %li, outbuflen = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) outbuflen);
return(-1);
}
# endif
#endif
/* We must remove the "7C" tag to get to the signature */
switch (id_type) {
case CACKEY_ID_TYPE_PIV:
outbuf_len = retval;
outbuf_p = cackey_read_bertlv_tag(outbuf, &outbuf_len, 0x7C, NULL, &outbuf_len);
if (outbuf_p == NULL) {
CACKEY_DEBUG_PRINTF("Response from PIV for GENERATE AUTHENTICATION was not a 0x7C tag, returning in failure");
return(-1);
}
retval = outbuf_len;
outbuf_len = retval;
outbuf_p = cackey_read_bertlv_tag(outbuf, &outbuf_len, 0x82, NULL, &outbuf_len);
if (outbuf_p == NULL) {
CACKEY_DEBUG_PRINTF("Response from PIV for GENERATE AUTHENTICATION was not a 0x82 within a 0x7C tag, returning in failure");
return(-1);
}
retval = outbuf_len;
break;
case CACKEY_ID_TYPE_CAC:
case CACKEY_ID_TYPE_CERT_ONLY:
break;
}
/* Unpad reply */
if (unpadOutput) {
if (retval < 3) {
CACKEY_DEBUG_PRINTF("Reply is too small, we are not able to unpad -- passing back and hoping for the best!");
CACKEY_DEBUG_PRINTF("Returning in success, retval = %li (bytes)", (long) retval);
return(retval);
}
if (outbuf[0] != 0x00) {
CACKEY_DEBUG_PRINTF("Unrecognized padding scheme -- passing back and hoping for the best!");
CACKEY_DEBUG_PRINTF("Returning in success, retval = %li (bytes)", (long) retval);
return(retval);
}
blocktype = outbuf[1];
unpadoffset = 0;
switch (blocktype) {
case 0x00:
/* Padding Scheme 1, the first non-zero byte is the start of data */
for (unpadoffset = 2; unpadoffset < retval; unpadoffset++) {
if (outbuf[unpadoffset] != 0x00) {
break;
}
}
break;
case 0x01:
/* Padding Scheme 2, pad bytes are 0xFF followed by 0x00 */
for (unpadoffset = 2; unpadoffset < retval; unpadoffset++) {
if (outbuf[unpadoffset] != 0xFF) {
if (outbuf[unpadoffset] == 0x00) {
unpadoffset++;
break;
} else {
CACKEY_DEBUG_PRINTF("Invalid padding data found, returning in failure, should have been 0x00 found 0x%02x", (unsigned int) outbuf[unpadoffset]);
return(-1);
}
} else {
CACKEY_DEBUG_PRINTF("Invalid padding data found, returning in failure, should have been 0xFF found 0x%02x", (unsigned int) outbuf[unpadoffset]);
return(-1);
}
}
break;
case 0x02:
/* Padding Scheme 3, pad bytes are non-zero first zero byte found is the seperator byte */
for (unpadoffset = 2; unpadoffset < retval; unpadoffset++) {
if (outbuf[unpadoffset] == 0x00) {
unpadoffset++;
break;
}
}
break;
}
if (unpadoffset > retval) {
CACKEY_DEBUG_PRINTF("Offset greater than reply size, aborting. (unpadoffset = %lu, retval = %lu)", (unsigned long) unpadoffset, (unsigned long) retval);
return(-1);
}
CACKEY_DEBUG_PRINTBUF("Padded:", outbuf, retval);
retval -= unpadoffset;
memmove(outbuf, outbuf + unpadoffset, retval);
CACKEY_DEBUG_PRINTBUF("Unpadded:", outbuf, retval);
}
CACKEY_DEBUG_PRINTF("Returning in success, retval = %li (bytes)", (long) retval);
return(retval);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static cackey_ret cackey_token_present(struct cackey_slot *slot) {
cackey_ret pcsc_connect_ret;
DWORD reader_len = 0, state = 0, protocol = 0, atr_len;
BYTE atr[MAX_ATR_SIZE];
LONG status_ret, scard_reconn_ret;
LPSTR *reader_name;
CACKEY_DEBUG_PRINTF("Called.");
if (slot->internal) {
CACKEY_DEBUG_PRINTF("Returning token present (internal token)");
return(CACKEY_PCSC_S_TOKENPRESENT);
}
pcsc_connect_ret = cackey_connect_card(slot);
if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");
return(CACKEY_PCSC_E_TOKENABSENT);
}
CACKEY_DEBUG_PRINTF("Calling SCardStatus() to determine card status");
atr_len = sizeof(atr);
status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
if (status_ret == SCARD_E_INSUFFICIENT_BUFFER) {
CACKEY_DEBUG_PRINTF("ScardStatus() returned SCARD_E_INSUFFICIENT_BUFFER, assuming this is a bug (e.g., Google PCSC) implementation and retrying");
atr_len = sizeof(atr);
reader_len = 32768;
reader_name = malloc(reader_len);
status_ret = SCardStatus(slot->pcsc_card, reader_name, &reader_len, &state, &protocol, atr, &atr_len);
free(reader_name);
}
if (status_ret == SCARD_E_INVALID_HANDLE) {
CACKEY_DEBUG_PRINTF("SCardStatus() returned SCARD_E_INVALID_HANDLE, marking as not already connected and trying again");
cackey_mark_slot_reset(slot);
pcsc_connect_ret = cackey_connect_card(slot);
if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");
return(CACKEY_PCSC_E_TOKENABSENT);
}
CACKEY_DEBUG_PRINTF("Calling SCardStatus() again");
atr_len = sizeof(atr);
status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
}
if (status_ret != SCARD_S_SUCCESS) {
cackey_mark_slot_reset(slot);
if (status_ret == SCARD_W_RESET_CARD) {
CACKEY_DEBUG_PRINTF("Reset required, please hold...");
scard_reconn_ret = cackey_reconnect_card(slot, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1);
if (scard_reconn_ret == SCARD_S_SUCCESS) {
/* Re-establish transaction, if it was present */
if (slot->transaction_depth > 0) {
slot->transaction_depth--;
slot->transaction_need_hw_lock = 1;
cackey_begin_transaction(slot);
}
CACKEY_DEBUG_PRINTF("Reset successful, requerying");
status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
if (status_ret != SCARD_S_SUCCESS) {
CACKEY_DEBUG_PRINTF("Still unable to query card status, returning token absent. SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));
return(CACKEY_PCSC_E_TOKENABSENT);
}
} else {
CACKEY_DEBUG_PRINTF("Unable to reconnect to card, returning token absent. SCardReconnect() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_reconn_ret));
return(CACKEY_PCSC_E_TOKENABSENT);
}
} else {
CACKEY_DEBUG_PRINTF("Unable to query card status, returning token absent. SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));
return(CACKEY_PCSC_E_TOKENABSENT);
}
}
if ((state & SCARD_ABSENT) == SCARD_ABSENT) {
CACKEY_DEBUG_PRINTF("Card is absent, returning token absent");
return(CACKEY_PCSC_E_TOKENABSENT);
}
CACKEY_DEBUG_PRINTF("Returning token present.");
return(CACKEY_PCSC_S_TOKENPRESENT);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static cackey_ret cackey_set_pin(struct cackey_slot *slot, unsigned char *old_pin, unsigned long old_pin_len, unsigned char *pin, unsigned long pin_len) {
struct cackey_pcsc_identity *pcsc_identities;
unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char old_cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char pin_update[sizeof(cac_pin) + sizeof(old_cac_pin)];
unsigned long num_certs;
uint16_t response_code;
int tries_remaining;
int send_ret;
int key_reference = 0x00;
/* Apparently, CAC PINs are *EXACTLY* 8 bytes long -- pad with 0xFF if too short */
if (pin_len >= 8) {
memcpy(cac_pin, pin, 8);
} else {
memcpy(cac_pin, pin, pin_len);
}
if (old_pin_len >= 8) {
memcpy(old_cac_pin, old_pin, 8);
} else {
memcpy(old_cac_pin, old_pin, old_pin_len);
}
/* Concatenate both PINs together to send as a single instruction */
memcpy(pin_update, old_cac_pin, sizeof(old_cac_pin));
memcpy(pin_update + sizeof(old_cac_pin), cac_pin, sizeof(cac_pin));
/* Reject PINs which are too short */
if (pin_len < 5) {
CACKEY_DEBUG_PRINTF("Rejecting New PIN which is too short (length = %lu, must be atleast 5)", pin_len);
return(CACKEY_PCSC_E_BADPIN);
}
if (old_pin_len < 5) {
CACKEY_DEBUG_PRINTF("Rejecting Old PIN which is too short (length = %lu, must be atleast 5)", old_pin_len);
return(CACKEY_PCSC_E_BADPIN);
}
/* PIV authentication uses a "key_reference" of 0x80 */
pcsc_identities = cackey_read_certs(slot, NULL, &num_certs);
if (num_certs > 0 && pcsc_identities != NULL) {
switch (pcsc_identities[0].id_type) {
case CACKEY_ID_TYPE_PIV:
CACKEY_DEBUG_PRINTF("We have PIV card, so we will attempt to authenticate using the PIV Application key reference");
key_reference = 0x80;
break;
default:
break;
}
cackey_free_certs(pcsc_identities, num_certs, 1);
}
/* Issue a Set PIN (CHANGE REFERENCE) */
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_CHANGE_REFERENCE, 0x00, key_reference, sizeof(pin_update), pin_update, 0x00, &response_code, NULL, NULL);
if (send_ret != CACKEY_PCSC_S_OK) {
if ((response_code & 0x63C0) == 0x63C0) {
tries_remaining = (response_code & 0xF);
CACKEY_DEBUG_PRINTF("PIN Verification failed, %i tries remaining", tries_remaining);
return(CACKEY_PCSC_E_BADPIN);
}
if (response_code == 0x6983) {
CACKEY_DEBUG_PRINTF("Unable to set PIN, device is locked or changing the PIN is disabled");
return(CACKEY_PCSC_E_LOCKED);
}
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("PIN Change succeeded");
return(CACKEY_PCSC_S_OK);
/* Disable a warning, since this is only used in debug mode */
tries_remaining = tries_remaining;
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static cackey_ret cackey_login(struct cackey_slot *slot, unsigned char *pin, unsigned long pin_len, int *tries_remaining_p, int retries) {
struct cackey_pcsc_identity *pcsc_identities;
unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned long num_certs;
uint16_t response_code;
int tries_remaining;
int send_ret;
int key_reference = 0x00, have_piv = 0;
cackey_ret connect_ret, token_ret;
/* Indicate that we do not know about how many tries are remaining */
if (tries_remaining_p) {
*tries_remaining_p = -1;
}
/* Apparently, CAC PINs are *EXACTLY* 8 bytes long -- pad with 0xFF if too short */
if (pin_len >= 8) {
memcpy(cac_pin, pin, 8);
} else {
memcpy(cac_pin, pin, pin_len);
}
/* Reject PINs which are too short */
if (pin_len < 5) {
CACKEY_DEBUG_PRINTF("Rejecting PIN which is too short (length = %lu, must be atleast 5)", pin_len);
return(CACKEY_PCSC_E_BADPIN);
}
/* PIV authentication uses a "key_reference" of 0x80 */
pcsc_identities = cackey_read_certs(slot, NULL, &num_certs);
if (num_certs > 0 && pcsc_identities != NULL) {
switch (pcsc_identities[0].id_type) {
case CACKEY_ID_TYPE_PIV:
CACKEY_DEBUG_PRINTF("We have PIV card, so we will attempt to authenticate using the PIV Application key reference");
have_piv = 1;
break;
default:
break;
}
cackey_free_certs(pcsc_identities, num_certs, 1);
}
if (have_piv == 1) {
key_reference = 0x80;
}
/* Issue PIN Verify */
send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_VERIFY, 0x00, key_reference, sizeof(cac_pin), cac_pin, 0x00, &response_code, NULL, NULL);
if (send_ret != CACKEY_PCSC_S_OK) {
if ((response_code & 0x63C0) == 0x63C0) {
tries_remaining = (response_code & 0xF);
CACKEY_DEBUG_PRINTF("PIN Verification failed, %i tries remaining", tries_remaining);
if (tries_remaining_p) {
*tries_remaining_p = tries_remaining;
}
return(CACKEY_PCSC_E_BADPIN);
}
if (response_code == 0x6983) {
CACKEY_DEBUG_PRINTF("PIN Verification failed, device is locked");
return(CACKEY_PCSC_E_LOCKED);
}
if (response_code == 0x6d00) {
if (retries > 0) {
CACKEY_DEBUG_PRINTF("Got ISO 7816 Response \"6D 00\" in response to a VERIFY request.");
CACKEY_DEBUG_PRINTF("We did not expect this because it is not mentioned in NIST SP 800-73-3 Part 2 Section 3.2.1 or GSC-IS v2.1");
CACKEY_DEBUG_PRINTF("We are going to try to reset the card and select the applet again.");
if (num_certs > 0 && pcsc_identities != NULL) {
cackey_detect_and_select_root_applet(slot, pcsc_identities[0].id_type);
}
cackey_mark_slot_reset(slot);
connect_ret = cackey_connect_card(slot);
if (connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Unable to reconnect after resetting the card, returning in error.");
return(connect_ret);
}
CACKEY_DEBUG_PRINTF("Verifying we still have a token.");
token_ret = cackey_token_present(slot);
if (token_ret != CACKEY_PCSC_S_TOKENPRESENT) {
CACKEY_DEBUG_PRINTF("Token not present, returning in error.");
return(token_ret);
}
CACKEY_DEBUG_PRINTF("Trying to login again");
return(cackey_login(slot, pin, pin_len, tries_remaining_p, retries - 1));
}
}
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("PIN Verification succeeded");
return(CACKEY_PCSC_S_OK);
}
/*
* SYNPOSIS
* ...
*
* ARGUMENTS
* ...
*
* RETURN VALUE
* ...
*
* NOTES
* ...
*
*/
static ssize_t cackey_pcsc_identity_to_label(struct cackey_pcsc_identity *identity, unsigned char *label_buf, unsigned long label_buf_len) {
unsigned long certificate_len;
void *label_asn1;
void *certificate;
int x509_read_ret;
certificate = identity->certificate;
certificate_len = identity->certificate_len;
if (certificate_len < 0) {
return(-1);
}
x509_read_ret = x509_to_subject(certificate, certificate_len, (void **) &label_asn1);
if (x509_read_ret < 0) {
return(-1);
}
x509_read_ret = x509_dn_to_string(label_asn1, x509_read_ret, (char *) label_buf, label_buf_len, "CN");
if (x509_read_ret <= 0) {
x509_read_ret = x509_dn_to_string(label_asn1, x509_read_ret, (char *) label_buf, label_buf_len, NULL);
if (x509_read_ret <= 0) {
return(-1);
}
}
#ifdef CACKEY_PARANOID
# ifdef _POSIX_SSIZE_MAX
if (x509_read_ret > _POSIX_SSIZE_MAX) {
CACKEY_DEBUG_PRINTF("x509_read_ret exceeds maximum value, returning in failure. (max = %li, x509_read_ret = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) x509_read_ret);
return(-1);
}
# endif
#endif
return(x509_read_ret);
}
/* Returns 0 on success */
static int cackey_mutex_create(void **mutex) {
pthread_mutex_t *pthread_mutex;
int pthread_retval;
CK_RV custom_retval;
CACKEY_DEBUG_PRINTF("Called.");
if ((cackey_args.flags & CKF_OS_LOCKING_OK) == CKF_OS_LOCKING_OK) {
pthread_mutex = malloc(sizeof(*pthread_mutex));
if (!pthread_mutex) {
CACKEY_DEBUG_PRINTF("Failed to allocate memory.");
return(-1);
}
pthread_retval = pthread_mutex_init(pthread_mutex, NULL);
if (pthread_retval != 0) {
CACKEY_DEBUG_PRINTF("pthread_mutex_init() returned error (%i).", pthread_retval);
return(-1);
}
*mutex = pthread_mutex;
} else {
if (cackey_args.CreateMutex) {
custom_retval = cackey_args.CreateMutex(mutex);
if (custom_retval != CKR_OK) {
CACKEY_DEBUG_PRINTF("cackey_args.CreateMutex() returned error (%li).", (long) custom_retval);
return(-1);
}
}
}
CACKEY_DEBUG_PRINTF("Returning sucessfully (0)");
return(0);
}
/* Returns 0 on success */
static int cackey_mutex_lock(void *mutex) {
pthread_mutex_t *pthread_mutex;
int pthread_retval;
CK_RV custom_retval;
CACKEY_DEBUG_PRINTF("Called.");
if ((cackey_args.flags & CKF_OS_LOCKING_OK) == CKF_OS_LOCKING_OK) {
pthread_mutex = mutex;
pthread_retval = pthread_mutex_lock(pthread_mutex);
if (pthread_retval != 0) {
CACKEY_DEBUG_PRINTF("pthread_mutex_lock() returned error (%i).", pthread_retval);
return(-1);
}
} else {
if (cackey_args.LockMutex) {
custom_retval = cackey_args.LockMutex(mutex);
if (custom_retval != CKR_OK) {
CACKEY_DEBUG_PRINTF("cackey_args.LockMutex() returned error (%li).", (long) custom_retval);
return(-1);
}
}
}
CACKEY_DEBUG_PRINTF("Returning sucessfully (0)");
return(0);
}
/* Returns 0 on success */
static int cackey_mutex_unlock(void *mutex) {
pthread_mutex_t *pthread_mutex;
int pthread_retval;
CK_RV custom_retval;
CACKEY_DEBUG_PRINTF("Called.");
if ((cackey_args.flags & CKF_OS_LOCKING_OK) == CKF_OS_LOCKING_OK) {
pthread_mutex = mutex;
pthread_retval = pthread_mutex_unlock(pthread_mutex);
if (pthread_retval != 0) {
CACKEY_DEBUG_PRINTF("pthread_mutex_unlock() returned error (%i).", pthread_retval);
return(-1);
}
} else {
if (cackey_args.UnlockMutex) {
custom_retval = cackey_args.UnlockMutex(mutex);
if (custom_retval != CKR_OK) {
CACKEY_DEBUG_PRINTF("cackey_args.UnlockMutex() returned error (%li).", (long) custom_retval);
return(-1);
}
}
}
CACKEY_DEBUG_PRINTF("Returning sucessfully (0)");
return(0);
}
static CK_ATTRIBUTE_PTR cackey_get_attributes(CK_OBJECT_CLASS objectclass, struct cackey_pcsc_identity *identity, unsigned long identity_num, CK_ULONG_PTR pulCount) {
static CK_BBOOL ck_true = 1;
static CK_BBOOL ck_false = 0;
static CK_TRUST ck_trusted = CK_TRUSTED_DELEGATOR;
CK_ULONG numattrs = 0, retval_count;
CK_ATTRIBUTE_TYPE curr_attr_type;
CK_ATTRIBUTE curr_attr, *retval;
CK_VOID_PTR pValue;
CK_ULONG ulValueLen;
CK_OBJECT_CLASS ck_object_class;
CK_CERTIFICATE_TYPE ck_certificate_type;
CK_KEY_TYPE ck_key_type;
CK_UTF8CHAR ucTmpBuf[1024];
SHA1Context sha1_ctx;
MD5_CTX md5_ctx;
uint8_t sha1_hash[SHA1HashSize];
uint8_t md5_hash[MD5HashSize];
unsigned char *certificate;
ssize_t certificate_len = -1, x509_read_ret;
int pValue_free;
CACKEY_DEBUG_PRINTF("Called (objectClass = %lu, identity_num = %lu).", (unsigned long) objectclass, identity_num);
*pulCount = 0;
if (objectclass != CKO_CERTIFICATE && objectclass != CKO_PUBLIC_KEY && objectclass != CKO_PRIVATE_KEY && objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF("Returning 0 objects (NULL), invalid object class");
return(NULL);
}
/* Get Cert */
if (identity == NULL) {
CACKEY_DEBUG_PRINTF("Returning 0 objects (NULL), invalid identiy provided");
return(NULL);
}
certificate = identity->certificate;
certificate_len = identity->certificate_len;
if (certificate_len == -1 || certificate == NULL) {
CACKEY_DEBUG_PRINTF("Returning 0 objects (NULL), this identity does not have an X.509 certificate associated with it and will not work");
return(NULL);
}
/* Verify that certificate is ASN.1 encoded X.509 certificate */
if (x509_to_serial(certificate, certificate_len, NULL) < 0) {
CACKEY_DEBUG_PRINTF("Returning 0 objects (NULL), the X.509 certificate associated with this identity is not valid");
return(NULL);
}
retval_count = 64;
retval = malloc(retval_count * sizeof(*retval));
for (curr_attr_type = 0; curr_attr_type < 0xce5363bf; curr_attr_type++) {
if (curr_attr_type == 0x800) {
curr_attr_type = 0xce536300;
}
pValue_free = 0;
pValue = NULL;
ulValueLen = (CK_LONG) -1;
switch (curr_attr_type) {
case CKA_CLASS:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_CLASS (0x%08lx) ...", (unsigned long) curr_attr_type);
ck_object_class = objectclass;
pValue = &ck_object_class;
ulValueLen = sizeof(ck_object_class);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_OBJECT_CLASS *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_TOKEN:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_TOKEN (0x%08lx) ...", (unsigned long) curr_attr_type);
pValue = &ck_true;
ulValueLen = sizeof(ck_true);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_PRIVATE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_PRIVATE (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a Netscape trust object");
break;
}
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_TRUSTED:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_TRUSTED (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
pValue = &ck_true;
ulValueLen = sizeof(ck_true);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_MODIFIABLE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_MODIFIABLE (0x%08lx) ...", (unsigned long) curr_attr_type);
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_LABEL:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_LABEL (0x%08lx) ...", (unsigned long) curr_attr_type);
if (identity->id_type == CACKEY_ID_TYPE_PIV) {
pValue = identity->card.piv.label;
ulValueLen = strlen(pValue);
} else {
ulValueLen = snprintf((char *) ucTmpBuf, sizeof(ucTmpBuf), "Identity #%lu", (unsigned long) identity_num);
pValue = ucTmpBuf;
if (ulValueLen >= sizeof(ucTmpBuf)) {
ulValueLen = 0;
pValue = NULL;
}
}
CACKEY_DEBUG_PRINTF(" ... returning (%p/%lu)", pValue, (unsigned long) ulValueLen);
break;
case CKA_VALUE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_VALUE (0x%08lx) ...", (unsigned long) curr_attr_type);
switch (objectclass) {
case CKO_PRIVATE_KEY:
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a private key.");
break;
case CKO_NETSCAPE_TRUST:
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
case CKO_PUBLIC_KEY:
if (certificate_len >= 0) {
x509_read_ret = x509_to_pubkey(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
break;
case CKO_CERTIFICATE:
pValue = certificate;
ulValueLen = certificate_len;
break;
}
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
case CKA_ISSUER:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_ISSUER (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_CERTIFICATE && objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a certificate or Netscape trust object");
break;
}
if (certificate_len >= 0) {
x509_read_ret = x509_to_issuer(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
case CKA_SERIAL_NUMBER:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_SERIAL_NUMBER (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_CERTIFICATE && objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a certificate or Netscape trust object");
break;
}
if (certificate_len >= 0) {
x509_read_ret = x509_to_serial(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
CACKEY_DEBUG_PRINTF(" ... returning (%p/%lu)", pValue, (unsigned long) ulValueLen);
break;
case CKA_SUBJECT:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_SUBJECT (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_CERTIFICATE) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a certificate");
break;
}
if (certificate_len >= 0) {
x509_read_ret = x509_to_subject(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
case CKA_ID:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_ID (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
ucTmpBuf[0] = ((identity_num + 1) >> 8) & 0xff;
ucTmpBuf[1] = (identity_num + 1) & 0xff;
pValue = &ucTmpBuf;
ulValueLen = 2;
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
case CKA_CERTIFICATE_TYPE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_CERTIFICATE_TYPE (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_CERTIFICATE) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a certificate.");
break;
}
/* We only support one certificate type */
ck_certificate_type = CKC_X_509;
pValue = &ck_certificate_type;
ulValueLen = sizeof(ck_certificate_type);
CACKEY_DEBUG_PRINTF(" ... returning CKC_X_509 (%lu) (%p/%lu)", (unsigned long) *((CK_CERTIFICATE_TYPE *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_KEY_TYPE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_KEY_TYPE (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_PRIVATE_KEY && objectclass != CKO_PUBLIC_KEY) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a key.");
break;
}
/* We only support one key type */
ck_key_type = CKK_RSA;
pValue = &ck_key_type;
ulValueLen = sizeof(ck_key_type);
CACKEY_DEBUG_PRINTF(" ... returning CKK_RSA (%lu) (%p/%lu)", (unsigned long) *((CK_CERTIFICATE_TYPE *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_SIGN:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_SIGN (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (objectclass == CKO_PRIVATE_KEY) {
pValue = &ck_true;
ulValueLen = sizeof(ck_true);
} else {
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
}
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_SIGN_RECOVER:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_SIGN_RECOVER (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
/* We currently only support "Sign with Appendix" */
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_DECRYPT:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_DECRYPT (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (objectclass == CKO_PRIVATE_KEY || objectclass == CKO_PUBLIC_KEY) {
pValue = &ck_true;
ulValueLen = sizeof(ck_true);
} else {
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
}
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_SENSITIVE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_SENSITIVE (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (objectclass == CKO_PRIVATE_KEY) {
pValue = &ck_true;
ulValueLen = sizeof(ck_true);
} else {
pValue = &ck_false;
ulValueLen = sizeof(ck_false);
}
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_EXTRACTABLE:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_EXTRACTABLE (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (objectclass == CKO_PRIVATE_KEY) {
pValue = &ck_false;
ulValueLen = sizeof(ck_true);
} else {
pValue = &ck_true;
ulValueLen = sizeof(ck_false);
}
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_BBOOL *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_MODULUS:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_MODULUS (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (certificate_len >= 0) {
x509_read_ret = x509_to_modulus(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
CACKEY_DEBUG_PRINTF(" ... returning (%p/%lu)", pValue, (unsigned long) ulValueLen);
break;
case CKA_PUBLIC_EXPONENT:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_PUBLIC_EXPONENT (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass == CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are a Netscape trust object");
break;
}
if (certificate_len >= 0) {
x509_read_ret = x509_to_exponent(certificate, certificate_len, &pValue);
if (x509_read_ret < 0) {
pValue = NULL;
} else {
ulValueLen = x509_read_ret;
}
}
CACKEY_DEBUG_PRINTF(" ... returning (%p/%lu)", pValue, (unsigned long) ulValueLen);
break;
case CKA_TRUST_DIGITAL_SIGNATURE:
case CKA_TRUST_NON_REPUDIATION:
case CKA_TRUST_KEY_ENCIPHERMENT:
case CKA_TRUST_DATA_ENCIPHERMENT:
case CKA_TRUST_KEY_AGREEMENT:
case CKA_TRUST_KEY_CERT_SIGN:
case CKA_TRUST_CRL_SIGN:
case CKA_TRUST_SERVER_AUTH:
case CKA_TRUST_CLIENT_AUTH:
case CKA_TRUST_CODE_SIGNING:
case CKA_TRUST_EMAIL_PROTECTION:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_TRUST_... (0x%08lx) ...", (unsigned long) curr_attr_type);
pValue = &ck_trusted;
ulValueLen = sizeof(ck_trusted);
CACKEY_DEBUG_PRINTF(" ... returning %lu (%p/%lu)", (unsigned long) *((CK_TRUST *) pValue), pValue, (unsigned long) ulValueLen);
break;
case CKA_CERT_SHA1_HASH:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_CERT_SHA1_HASH (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a Netscape trust object");
break;
}
SHA1Reset(&sha1_ctx);
SHA1Input(&sha1_ctx, certificate, certificate_len);
SHA1Result(&sha1_ctx, sha1_hash);
pValue = sha1_hash;
ulValueLen = sizeof(sha1_hash);
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
case CKA_CERT_MD5_HASH:
CACKEY_DEBUG_PRINTF("Requesting attribute CKA_CERT_MD5_HASH (0x%08lx) ...", (unsigned long) curr_attr_type);
if (objectclass != CKO_NETSCAPE_TRUST) {
CACKEY_DEBUG_PRINTF(" ... but not getting it because we are not a Netscape trust object");
break;
}
MD5Init(&md5_ctx);
MD5Update(&md5_ctx, certificate, certificate_len);
MD5Final(md5_hash, &md5_ctx);
pValue = md5_hash;
ulValueLen = sizeof(md5_hash);
CACKEY_DEBUG_PRINTF(" ... returning %p/%lu", pValue, (unsigned long) ulValueLen);
break;
default:
pValue = NULL;
ulValueLen = (CK_LONG) -1;
break;
}
if (((CK_LONG) ulValueLen) != ((CK_LONG) -1)) {
/* Push curr_attr onto the stack */
curr_attr.type = curr_attr_type;
curr_attr.ulValueLen = ulValueLen;
curr_attr.pValue = malloc(curr_attr.ulValueLen);
memcpy(curr_attr.pValue, pValue, curr_attr.ulValueLen);
if (pValue_free && pValue) {
free(pValue);
}
if (numattrs >= retval_count) {
retval = realloc(retval, retval_count * sizeof(*retval));
}
memcpy(&retval[numattrs], &curr_attr, sizeof(curr_attr));
numattrs++;
}
}
if (numattrs != 0) {
retval_count = numattrs;
retval = realloc(retval, retval_count * sizeof(*retval));
} else {
free(retval);
retval = NULL;
}
*pulCount = numattrs;
CACKEY_DEBUG_PRINTF("Returning %lu objects (%p).", numattrs, (void *) retval);
return(retval);
}
static void cackey_free_identities(struct cackey_identity *identities, unsigned long identities_count) {
CK_ATTRIBUTE *curr_attr;
unsigned long id_idx, attr_idx;
if (identities == NULL || identities_count == 0) {
return;
}
for (id_idx = 0; id_idx < identities_count; id_idx++) {
if (identities[id_idx].attributes) {
for (attr_idx = 0; attr_idx < identities[id_idx].attributes_count; attr_idx++) {
curr_attr = &identities[id_idx].attributes[attr_idx];
if (curr_attr->pValue) {
free(curr_attr->pValue);
}
}
if (identities[id_idx].attributes) {
free(identities[id_idx].attributes);
}
cackey_free_certs(identities[id_idx].pcsc_identity, 1, 1);
}
}
free(identities);
}
static unsigned long cackey_read_dod_identities(struct cackey_identity *identities, unsigned long num_dod_certs) {
unsigned long cert_idx, id_idx = 0;
if (identities == NULL) {
return(num_dod_certs * 3);
}
for (cert_idx = 0; cert_idx < num_dod_certs; cert_idx++) {
identities[id_idx].pcsc_identity = NULL;
identities[id_idx].attributes = cackey_get_attributes(CKO_CERTIFICATE, &extra_certs[cert_idx], 0xf000 | cert_idx, &identities[id_idx].attributes_count);
id_idx++;
identities[id_idx].pcsc_identity = NULL;
identities[id_idx].attributes = cackey_get_attributes(CKO_PUBLIC_KEY, &extra_certs[cert_idx], 0xf000 | cert_idx, &identities[id_idx].attributes_count);
id_idx++;
identities[id_idx].pcsc_identity = NULL;
identities[id_idx].attributes = cackey_get_attributes(CKO_NETSCAPE_TRUST, &extra_certs[cert_idx], 0xf000 | cert_idx, &identities[id_idx].attributes_count);
id_idx++;
}
return(id_idx);
}
static struct cackey_identity *cackey_read_identities(struct cackey_slot *slot, unsigned long *ids_found) {
struct cackey_pcsc_identity *pcsc_identities;
struct cackey_identity *identities;
unsigned long num_ids, id_idx, curr_id_type;
unsigned long num_certs, num_dod_certs, cert_idx;
int include_extra_certs = 0, include_dod_certs;
CACKEY_DEBUG_PRINTF("Called.");
if (ids_found == NULL) {
CACKEY_DEBUG_PRINTF("Error. ids_found is NULL");
return(NULL);
}
#ifdef CACKEY_CARD_SLOT_INCLUDE_EXTRA_CERTS
include_extra_certs = 1;
#endif
if (getenv("CACKEY_DOD_CERTS_ON_HW_SLOTS") != NULL) {
include_extra_certs = 1;
}
if (getenv("CACKEY_NO_DOD_CERTS_ON_HW_SLOTS") != NULL) {
include_extra_certs = 0;
}
#ifdef CACKEY_NO_EXTRA_CERTS
if (getenv("CACKEY_EXTRA_CERTS") != NULL) {
include_dod_certs = 1;
} else {
include_dod_certs = 0;
}
#else
if (getenv("CACKEY_NO_EXTRA_CERTS") != NULL) {
include_dod_certs = 0;
} else {
include_dod_certs = 1;
}
#endif
if (include_dod_certs) {
num_dod_certs = sizeof(extra_certs) / sizeof(extra_certs[0]);
} else {
num_dod_certs = 0;
}
if (slot->internal) {
num_ids = cackey_read_dod_identities(NULL, num_dod_certs);
if (num_ids != 0) {
identities = malloc(num_ids * sizeof(*identities));
cackey_read_dod_identities(identities, num_dod_certs);
} else {
identities = NULL;
}
*ids_found = num_ids;
return(identities);
}
pcsc_identities = cackey_read_certs(slot, NULL, &num_certs);
if (pcsc_identities != NULL) {
/* Convert number of Certs to number of objects */
num_ids = (CKO_PRIVATE_KEY - CKO_CERTIFICATE + 1) * num_certs;
if (include_extra_certs) {
num_ids += cackey_read_dod_identities(NULL, num_dod_certs);
}
identities = malloc(num_ids * sizeof(*identities));
/* Add certificates, public keys, and private keys from the smartcard */
id_idx = 0;
for (cert_idx = 0; cert_idx < num_certs; cert_idx++) {
for (curr_id_type = CKO_CERTIFICATE; curr_id_type <= CKO_PRIVATE_KEY; curr_id_type++) {
identities[id_idx].attributes = cackey_get_attributes(curr_id_type, &pcsc_identities[cert_idx], cert_idx, &identities[id_idx].attributes_count);
identities[id_idx].pcsc_identity = malloc(sizeof(*identities[id_idx].pcsc_identity));
memcpy(identities[id_idx].pcsc_identity, &pcsc_identities[cert_idx], sizeof(*identities[id_idx].pcsc_identity));
identities[id_idx].pcsc_identity->certificate = malloc(pcsc_identities[cert_idx].certificate_len);
memcpy(identities[id_idx].pcsc_identity->certificate, pcsc_identities[cert_idx].certificate, pcsc_identities[cert_idx].certificate_len);
id_idx++;
}
}
if (include_extra_certs) {
CACKEY_DEBUG_PRINTF("Including US Government Certificates on hardware slot");
cackey_read_dod_identities(identities + id_idx, num_dod_certs);
}
cackey_free_certs(pcsc_identities, num_certs, 1);
*ids_found = num_ids;
return(identities);
}
*ids_found = 0;
return(NULL);
}
static cackey_ret cackey_get_pin(char *pinbuf) {
FILE *pinfd;
char *fgets_ret;
int pclose_ret;
if (cackey_pin_command == NULL) {
return(CACKEY_PCSC_E_GENERIC);
}
if (pinbuf == NULL) {
return(CACKEY_PCSC_E_GENERIC);
}
CACKEY_DEBUG_PRINTF("CACKEY_PIN_COMMAND = %s", cackey_pin_command);
pinfd = popen(cackey_pin_command, "r");
if (pinfd == NULL) {
CACKEY_DEBUG_PRINTF("Error. %s: Unable to run", cackey_pin_command);
return(CACKEY_PCSC_E_BADPIN);
}
fgets_ret = fgets(pinbuf, 32, pinfd);
if (fgets_ret == NULL) {
pinbuf[0] = '\0';
}
pclose_ret = pclose(pinfd);
if (pclose_ret == -1 && errno == ECHILD) {
CACKEY_DEBUG_PRINTF("Notice. pclose() indicated it could not get the status of the child, assuming it succeeeded !");
pclose_ret = 0;
}
if (pclose_ret != 0) {
CACKEY_DEBUG_PRINTF("Error. %s: exited with non-zero status of %i", cackey_pin_command, pclose_ret);
return(CACKEY_PCSC_E_BADPIN);
}
if (strlen(pinbuf) < 1) {
CACKEY_DEBUG_PRINTF("Error. %s: returned no data", cackey_pin_command);
return(CACKEY_PCSC_E_BADPIN);
}
if (pinbuf[strlen(pinbuf) - 1] == '\n') {
pinbuf[strlen(pinbuf) - 1] = '\0';
}
return(CACKEY_PCSC_S_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_Initialize)(CK_VOID_PTR pInitArgs) {
CK_C_INITIALIZE_ARGS CK_PTR args;
uint32_t idx, highest_slot;
int mutex_init_ret;
int include_dod_certs;
CACKEY_DEBUG_PRINTF("Called.");
if (cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Already initialized.");
return(CKR_CRYPTOKI_ALREADY_INITIALIZED);
}
if (pInitArgs != NULL) {
args = pInitArgs;
memcpy(&cackey_args, args, sizeof(cackey_args));
if (args->CreateMutex == NULL || args->DestroyMutex == NULL || args->LockMutex == NULL || args->UnlockMutex == NULL) {
if (args->CreateMutex != NULL || args->DestroyMutex != NULL || args->LockMutex != NULL || args->UnlockMutex != NULL) {
CACKEY_DEBUG_PRINTF("Error. Some, but not All threading primitives provided.");
return(CKR_ARGUMENTS_BAD);
}
}
} else {
cackey_args.CreateMutex = NULL;
cackey_args.DestroyMutex = NULL;
cackey_args.LockMutex = NULL;
cackey_args.UnlockMutex = NULL;
cackey_args.flags = 0;
}
for (idx = 0; idx < (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])); idx++) {
cackey_sessions[idx].active = 0;
}
for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) {
cackey_slots[idx].active = 0;
cackey_slots[idx].pcsc_reader = NULL;
cackey_slots[idx].transaction_depth = 0;
cackey_slots[idx].transaction_need_hw_lock = 0;
cackey_slots[idx].slot_reset = 0;
cackey_slots[idx].token_flags = 0;
cackey_slots[idx].label = NULL;
cackey_slots[idx].internal = 0;
cackey_slots[idx].id_type_hint = CACKEY_ID_TYPE_UNKNOWN;
}
#ifdef CACKEY_NO_EXTRA_CERTS
if (getenv("CACKEY_EXTRA_CERTS") != NULL) {
include_dod_certs = 1;
} else {
include_dod_certs = 0;
}
#else
if (getenv("CACKEY_NO_EXTRA_CERTS") != NULL) {
include_dod_certs = 0;
} else {
include_dod_certs = 1;
}
#endif
if (include_dod_certs == 0) {
CACKEY_DEBUG_PRINTF("Asked not to include DoD certificates");
} else {
highest_slot = (sizeof(cackey_slots) / sizeof(cackey_slots[0])) - 1;
CACKEY_DEBUG_PRINTF("Including DoD certs in slot %lu", (unsigned long) highest_slot);
cackey_slots[highest_slot].active = 1;
cackey_slots[highest_slot].internal = 1;
cackey_slots[highest_slot].label = (unsigned char *) "US Government Certificates";
cackey_slots[highest_slot].pcsc_reader = "CACKey";
cackey_slots[highest_slot].token_flags = 0;
}
cackey_initialized = 1;
if (!cackey_biglock_init) {
mutex_init_ret = cackey_mutex_create(&cackey_biglock);
if (mutex_init_ret != 0) {
CACKEY_DEBUG_PRINTF("Error. Mutex initialization failed.");
return(CKR_CANT_LOCK);
}
cackey_biglock_init = 1;
}
/* Define a command to prompt user for a PIN */
#ifdef CACKEY_PIN_COMMAND_DEFAULT
cackey_pin_command = strdup(CACKEY_MACRO_DEFAULT_XSTR(CACKEY_PIN_COMMAND_DEFAULT));
#endif
#ifdef CACKEY_PIN_COMMAND_XONLY_DEFAULT
if (getenv("DISPLAY") != NULL) {
cackey_pin_command = strdup(CACKEY_MACRO_DEFAULT_XSTR(CACKEY_PIN_COMMAND_XONLY_DEFAULT));
}
#endif
if (getenv("CACKEY_PIN_COMMAND") != NULL) {
cackey_pin_command = strdup(getenv("CACKEY_PIN_COMMAND"));
}
if (getenv("CACKEY_PIN_COMMAND_XONLY") != NULL && getenv("DISPLAY") != NULL) {
cackey_pin_command = strdup(getenv("CACKEY_PIN_COMMAND_XONLY"));
}
#ifdef CACKEY_READERS_INCLUDE_ONLY_DEFAULT
cackey_readers_include_only = strdup(CACKEY_MACRO_DEFAULT_XSTR(CACKEY_READERS_INCLUDE_ONLY_DEFAULT));
#endif
#ifdef CACKEY_READERS_EXCLUDE_DEFAULT
cackey_readers_exclude = strdup(CACKEY_MACRO_DEFAULT_XSTR(CACKEY_READERS_EXCLUDE_DEFAULT));
#endif
if (getenv("CACKEY_READERS_INCLUDE_ONLY") != NULL) {
cackey_readers_include_only = strdup(getenv("CACKEY_READERS_INCLUDE_ONLY"));
if (cackey_readers_include_only[0] == '\0') {
free(cackey_readers_include_only);
cackey_readers_include_only = NULL;
}
}
if (getenv("CACKEY_READERS_EXCLUDE") != NULL) {
cackey_readers_exclude = strdup(getenv("CACKEY_READERS_EXCLUDE"));
if (cackey_readers_exclude[0] == '\0') {
free(cackey_readers_exclude);
cackey_readers_exclude = NULL;
}
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_Finalize)(CK_VOID_PTR pReserved) {
uint32_t idx;
CACKEY_DEBUG_PRINTF("Called.");
if (pReserved != NULL) {
CACKEY_DEBUG_PRINTF("Error. pReserved is not NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
for (idx = 0; idx < (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])); idx++) {
if (cackey_sessions[idx].active) {
C_CloseSession(idx);
}
}
cackey_slots_disconnect_all(1);
for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) {
if (cackey_slots[idx].internal) {
continue;
}
if (cackey_slots[idx].cached_certs) {
cackey_free_certs(cackey_slots[idx].cached_certs, cackey_slots[idx].cached_certs_count, 1);
cackey_slots[idx].cached_certs = NULL;
}
}
cackey_pcsc_disconnect();
if (cackey_pin_command != NULL) {
free(cackey_pin_command);
cackey_pin_command = NULL;
}
if (cackey_readers_include_only != NULL) {
free(cackey_readers_include_only);
cackey_readers_include_only = NULL;
}
if (cackey_readers_exclude != NULL) {
free(cackey_readers_exclude);
cackey_readers_exclude = NULL;
}
cackey_initialized = 0;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)(CK_INFO_PTR pInfo) {
static CK_UTF8CHAR manufacturerID[] = "U.S. Government";
static CK_UTF8CHAR libraryDescription[] = "CACKey";
CACKEY_DEBUG_PRINTF("Called.");
if (pInfo == NULL) {
CACKEY_DEBUG_PRINTF("Error. pInfo is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
pInfo->cryptokiVersion.major = ((CACKEY_CRYPTOKI_VERSION_CODE) >> 16) & 0xff;
pInfo->cryptokiVersion.minor = ((CACKEY_CRYPTOKI_VERSION_CODE) >> 8) & 0xff;
memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID));
memcpy(pInfo->manufacturerID, manufacturerID, sizeof(manufacturerID) - 1);
pInfo->flags = 0x00;
memset(pInfo->libraryDescription, ' ', sizeof(pInfo->libraryDescription));
memcpy(pInfo->libraryDescription, libraryDescription, sizeof(libraryDescription) - 1);
pInfo->libraryVersion.major = (cackey_getversion() >> 16) & 0xff;
pInfo->libraryVersion.minor = (cackey_getversion() >> 8) & 0xff;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
/*
* Process list of readers, and create mapping between reader name and slot ID
*/
CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
static int first_call = 1;
int mutex_retval;
int pcsc_connect_ret;
CK_ULONG count, slot_count = 0, currslot, slot_idx;
char *pcsc_readers, *pcsc_readers_s, *pcsc_readers_e;
char *reader_check_pattern;
DWORD pcsc_readers_len;
LONG scard_listreaders_ret;
size_t curr_reader_len;
int slot_reset;
int include_reader;
CACKEY_DEBUG_PRINTF("Called.");
if (pulCount == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulCount is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
/* Clear list of slots */
slot_reset = 0;
if (pSlotList) {
if (first_call) {
first_call = 0;
slot_reset = 1;
}
/* If any of the slots have been reset then purge all information and check again */
for (currslot = 0; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) {
if (cackey_slots[currslot].internal) {
continue;
}
if (!cackey_slots[currslot].active) {
continue;
}
if (cackey_slots[currslot].slot_reset) {
slot_reset = 1;
break;
}
}
if (slot_reset) {
CACKEY_DEBUG_PRINTF("Purging all slot information.");
/* Only update the list of slots if we are actually being supply the slot information */
cackey_slots_disconnect_all(1);
for (currslot = 0; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) {
if (cackey_slots[currslot].internal) {
continue;
}
cackey_slots[currslot].active = 0;
}
}
}
/* Determine list of readers */
pcsc_connect_ret = cackey_pcsc_connect();
if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Connection to PC/SC failed, assuming no hardware slots");
} else {
pcsc_readers_len = 0;
scard_listreaders_ret = SCardListReaders(*cackey_pcsc_handle, NULL, NULL, &pcsc_readers_len);
if (scard_listreaders_ret == SCARD_F_COMM_ERROR) {
CACKEY_DEBUG_PRINTF("Error. SCardListReaders() returned SCARD_F_COMM_ERROR, assuming Connection to PC/SC went away. Reconnecting.");
cackey_pcsc_disconnect();
cackey_pcsc_connect();
CACKEY_DEBUG_PRINTF("Trying SCardListReaders() again");
scard_listreaders_ret = SCardListReaders(*cackey_pcsc_handle, NULL, NULL, &pcsc_readers_len);
}
if (scard_listreaders_ret == SCARD_E_INSUFFICIENT_BUFFER) {
CACKEY_DEBUG_PRINTF("Error. SCardListReaders() returned SCARD_E_INSUFFICIENT_BUFFER, assuming this is a bug (e.g., Google PCSC) and allocating a massive amount of space to hold the reader list.");
pcsc_readers_len = 32768;
scard_listreaders_ret = SCARD_S_SUCCESS;
}
if (scard_listreaders_ret == SCARD_S_SUCCESS && pcsc_readers_len != 0) {
pcsc_readers = malloc(pcsc_readers_len);
pcsc_readers_s = pcsc_readers;
scard_listreaders_ret = SCardListReaders(*cackey_pcsc_handle, NULL, pcsc_readers, &pcsc_readers_len);
if (scard_listreaders_ret == SCARD_S_SUCCESS) {
pcsc_readers_e = pcsc_readers + pcsc_readers_len;
/* Start with Slot ID 1, to avoid a bug in GDM on RHEL */
/* Bug 594911: https://bugzilla.redhat.com/show_bug.cgi?id=594911 */
currslot = 1;
while (pcsc_readers < pcsc_readers_e) {
/* Find next available slot */
for (; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) {
if (!cackey_slots[currslot].active) {
break;
}
}
curr_reader_len = strlen(pcsc_readers);
if ((pcsc_readers + curr_reader_len) > pcsc_readers_e) {
break;
}
if (curr_reader_len == 0) {
break;
}
if (currslot >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Found more readers than slots are available!");
break;
}
CACKEY_DEBUG_PRINTF("Found reader: %s (currslot = %lu)", pcsc_readers, (unsigned long) currslot);
if (cackey_readers_include_only != NULL) {
CACKEY_DEBUG_PRINTF("Asked to include only readers matching: %s", cackey_readers_include_only);
include_reader = 0;
reader_check_pattern = cackey_readers_include_only;
} else if (cackey_readers_exclude != NULL) {
CACKEY_DEBUG_PRINTF("Asked to exclude readers matching: %s", cackey_readers_exclude);
include_reader = 1;
reader_check_pattern = cackey_readers_exclude;
} else {
include_reader = 1;
reader_check_pattern = NULL;
}
if (reader_check_pattern != NULL) {
if (strstr(pcsc_readers, reader_check_pattern) != NULL) {
CACKEY_DEBUG_PRINTF("This reader matched the pattern.");
include_reader = !include_reader;
}
}
if (include_reader != 1) {
CACKEY_DEBUG_PRINTF("Skipping this reader.");
pcsc_readers += curr_reader_len + 1;
continue;
}
/* Only update the list of slots if we are actually being asked supply the slot information */
if (pSlotList) {
if (slot_reset) {
cackey_slots[currslot].active = 1;
cackey_slots[currslot].internal = 0;
cackey_slots[currslot].pcsc_reader = strdup(pcsc_readers);
cackey_slots[currslot].pcsc_card_connected = 0;
cackey_slots[currslot].transaction_depth = 0;
cackey_slots[currslot].transaction_need_hw_lock = 0;
if (cackey_pin_command == NULL) {
cackey_slots[currslot].token_flags = CKF_LOGIN_REQUIRED;
} else {
cackey_slots[currslot].token_flags = 0;
}
cackey_slots[currslot].label = NULL;
cackey_mark_slot_reset(&cackey_slots[currslot]);
}
} else {
if (!cackey_slots[currslot].active) {
/* Artificially increase the number of active slots by what will become active */
CACKEY_DEBUG_PRINTF("Found in-active slot %lu, but it will be active after a reset -- marking as active for accounting purposes", (unsigned long) currslot);
slot_count++;
}
}
currslot++;
pcsc_readers += curr_reader_len + 1;
}
} else {
CACKEY_DEBUG_PRINTF("Second call to SCardListReaders failed, return %s/%li", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_listreaders_ret), (long) scard_listreaders_ret);
}
free(pcsc_readers_s);
} else {
CACKEY_DEBUG_PRINTF("First call to SCardListReaders failed, return %s/%li", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_listreaders_ret), (long) scard_listreaders_ret);
}
}
for (currslot = 0; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) {
if (cackey_slots[currslot].active) {
CACKEY_DEBUG_PRINTF("Found active slot %lu, reader = %s", (unsigned long) currslot, cackey_slots[currslot].pcsc_reader);
slot_count++;
}
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
if (pSlotList == NULL) {
*pulCount = slot_count;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i). Found %lu readers, but not storing IDs (pSlotList == NULL)", CKR_OK, (unsigned long) slot_count);
return(CKR_OK);
}
count = *pulCount;
if (count < slot_count) {
CACKEY_DEBUG_PRINTF("Error. User allocated %lu entries, but we have %lu entries.", count, slot_count);
CACKEY_DEBUG_PRINTF("Returning CKR_BUFFER_TOO_SMALL");
return(CKR_BUFFER_TOO_SMALL);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
slot_idx = 0;
for (currslot = 0; (currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0]))); currslot++) {
if (!cackey_slots[currslot].active) {
continue;
}
if (slot_idx >= count) {
CACKEY_DEBUG_PRINTF("Error. User allocated %lu entries, but we just tried to write to the %lu index -- ignoring", count, slot_idx);
continue;
}
pSlotList[slot_idx] = currslot;
slot_idx++;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
*pulCount = slot_count;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i). Found %lu readers.", CKR_OK, (unsigned long) slot_count);
return(CKR_OK);
tokenPresent = tokenPresent; /* Supress unused variable warning */
}
CK_DEFINE_FUNCTION(CK_RV, C_GetSlotInfo)(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
static CK_UTF8CHAR slotDescription[] = "CACKey Slot";
int mutex_retval;
int bytes_to_copy;
CACKEY_DEBUG_PRINTF("Called.");
if (pInfo == NULL) {
CACKEY_DEBUG_PRINTF("Error. pInfo is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_SLOT_ID_INVALID);
}
pInfo->flags = CKF_HW_SLOT;
if (!cackey_slots[slotID].internal) {
pInfo->flags |= CKF_REMOVABLE_DEVICE;
}
if (cackey_token_present(&cackey_slots[slotID]) == CACKEY_PCSC_S_TOKENPRESENT) {
pInfo->flags |= CKF_TOKEN_PRESENT;
}
bytes_to_copy = strlen(cackey_slots[slotID].pcsc_reader);
if (sizeof(pInfo->manufacturerID) < bytes_to_copy) {
bytes_to_copy = sizeof(pInfo->manufacturerID);
}
memcpy(pInfo->manufacturerID, cackey_slots[slotID].pcsc_reader, bytes_to_copy);
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
memset(pInfo->slotDescription, ' ', sizeof(pInfo->slotDescription));
memcpy(pInfo->slotDescription, slotDescription, sizeof(slotDescription) - 1);
memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID));
pInfo->hardwareVersion.major = (cackey_getversion() >> 16) & 0xff;
pInfo->hardwareVersion.minor = (cackey_getversion() >> 8) & 0xff;
pInfo->firmwareVersion.major = 0x00;
pInfo->firmwareVersion.minor = 0x00;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo)(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
static CK_UTF8CHAR manufacturerID[] = "U.S. Government";
static CK_UTF8CHAR defaultLabel[] = "Unknown Token";
static CK_UTF8CHAR model[] = "CAC Token";
struct cackey_pcsc_identity *pcsc_identities;
unsigned long num_certs;
ssize_t label_ret;
int mutex_retval;
int use_default_label;
CACKEY_DEBUG_PRINTF("Called.");
if (pInfo == NULL) {
CACKEY_DEBUG_PRINTF("Error. pInfo is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_SLOT_ID_INVALID);
}
if (cackey_token_present(&cackey_slots[slotID]) != CACKEY_PCSC_S_TOKENPRESENT) {
CACKEY_DEBUG_PRINTF("No token is present in slotID = %lu", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_TOKEN_NOT_PRESENT);
}
/* Determine token label from certificates */
memset(pInfo->label, ' ', sizeof(pInfo->label));
use_default_label = 1;
if (cackey_slots[slotID].label == NULL) {
pcsc_identities = cackey_read_certs(&cackey_slots[slotID], NULL, &num_certs);
if (pcsc_identities != NULL) {
if (num_certs > 0) {
label_ret = cackey_pcsc_identity_to_label(pcsc_identities, pInfo->label, sizeof(pInfo->label));
if (label_ret > 0) {
use_default_label = 0;
cackey_slots[slotID].label = malloc(sizeof(pInfo->label));
memcpy(cackey_slots[slotID].label, pInfo->label, sizeof(pInfo->label));
}
}
cackey_free_certs(pcsc_identities, num_certs, 1);
}
} else {
memcpy(pInfo->label, cackey_slots[slotID].label, sizeof(pInfo->label));
use_default_label = 0;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
if (use_default_label) {
memcpy(pInfo->label, defaultLabel, sizeof(defaultLabel) - 1);
}
memset(pInfo->manufacturerID, ' ', sizeof(pInfo->manufacturerID));
memcpy(pInfo->manufacturerID, manufacturerID, sizeof(manufacturerID) - 1);
memset(pInfo->model, ' ', sizeof(pInfo->model));
memcpy(pInfo->model, model, sizeof(model) - 1);
memset(pInfo->serialNumber, ' ', sizeof(pInfo->serialNumber));
memset(pInfo->utcTime, ' ', sizeof(pInfo->utcTime));
pInfo->hardwareVersion.major = (cackey_getversion() >> 16) & 0xff;
pInfo->hardwareVersion.minor = (cackey_getversion() >> 8) & 0xff;
pInfo->firmwareVersion.major = 0x00;
pInfo->firmwareVersion.minor = 0x00;
pInfo->flags = CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED | cackey_slots[slotID].token_flags;
if (cackey_pin_command != NULL) {
pInfo->flags |= CKF_PROTECTED_AUTHENTICATION_PATH;
}
pInfo->ulMaxSessionCount = (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])) - 1;
pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION;
pInfo->ulMaxRwSessionCount = 0;
pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION;
pInfo->ulMaxPinLen = 128;
pInfo->ulMinPinLen = 0;
pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_WaitForSlotEvent)(CK_FLAGS flags, CK_SLOT_ID_PTR pSlotID, CK_VOID_PTR pReserved) {
CACKEY_DEBUG_PRINTF("Called.");
if (pReserved != NULL) {
CACKEY_DEBUG_PRINTF("Error. pReserved is not NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* XXX: TODO: Implement this... */
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismList)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pulCount == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulCount is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (pMechanismList == NULL) {
*pulCount = 1;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
if (*pulCount < 1) {
CACKEY_DEBUG_PRINTF("Error. Buffer too small.");
return(CKR_BUFFER_TOO_SMALL);
}
pMechanismList[0] = CKM_RSA_PKCS;
*pulCount = 1;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismInfo)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) {
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (pInfo == NULL) {
CACKEY_DEBUG_PRINTF("Error. pInfo is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
switch (type) {
case CKM_RSA_PKCS:
pInfo->ulMinKeySize = 512;
pInfo->ulMaxKeySize = 8192;
pInfo->flags = CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY;
break;
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
/* We don't support this method. */
CK_DEFINE_FUNCTION(CK_RV, C_InitToken)(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_TOKEN_WRITE_PROTECTED (%i)", CKR_TOKEN_WRITE_PROTECTED);
return(CKR_TOKEN_WRITE_PROTECTED);
}
/* We don't support this method. */
CK_DEFINE_FUNCTION(CK_RV, C_InitPIN)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_TOKEN_WRITE_PROTECTED (%i)", CKR_TOKEN_WRITE_PROTECTED);
return(CKR_TOKEN_WRITE_PROTECTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SetPIN)(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen) {
char oldpinbuf[64], newpinbuf[64];
cackey_ret set_pin_ret, get_pin_ret;
CK_SLOT_ID slotID;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
if (cackey_pin_command != NULL) {
/* Get old PIN */
get_pin_ret = cackey_get_pin(oldpinbuf);
if (get_pin_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Error while getting Old PIN, returning CKR_PIN_INCORRECT.");
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_INCORRECT);
}
pOldPin = (CK_UTF8CHAR_PTR) oldpinbuf;
ulOldPinLen = strlen(oldpinbuf);
/* Get new PIN */
get_pin_ret = cackey_get_pin(newpinbuf);
if (get_pin_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("Error while getting New PIN, returning CKR_PIN_INVALID.");
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_INVALID);
}
pNewPin = (CK_UTF8CHAR_PTR) newpinbuf;
ulNewPinLen = strlen(newpinbuf);
}
if (pOldPin == NULL) {
CACKEY_DEBUG_PRINTF("Old PIN value is wrong (null).");
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_INCORRECT);
}
if (ulOldPinLen == 0 || ulOldPinLen > 8) {
CACKEY_DEBUG_PRINTF("Old PIN length is wrong: %lu.", (unsigned long) ulOldPinLen);
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_INCORRECT);
}
if (pNewPin == NULL) {
CACKEY_DEBUG_PRINTF("New PIN value is wrong (either NULL, or too long/short).");
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_INVALID);
}
if (ulNewPinLen < 5 || ulNewPinLen > 8) {
CACKEY_DEBUG_PRINTF("New PIN length is wrong: %lu, must be atleast 5 and no more than 8.", (unsigned long) ulNewPinLen);
cackey_mutex_unlock(cackey_biglock);
return(CKR_PIN_LEN_RANGE);
}
set_pin_ret = cackey_set_pin(&cackey_slots[slotID], pOldPin, ulOldPinLen, pNewPin, ulNewPinLen);
if (set_pin_ret != CACKEY_PCSC_S_OK) {
if (cackey_pin_command == NULL) {
cackey_slots[slotID].token_flags |= CKF_LOGIN_REQUIRED;
}
if (set_pin_ret == CACKEY_PCSC_E_LOCKED) {
cackey_slots[slotID].token_flags |= CKF_USER_PIN_LOCKED;
}
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
switch (set_pin_ret) {
case CACKEY_PCSC_S_OK:
CACKEY_DEBUG_PRINTF("Successfully set PIN.");
return(CKR_OK);
case CACKEY_PCSC_E_BADPIN:
CACKEY_DEBUG_PRINTF("PIN was invalid.");
return(CKR_PIN_INVALID);
case CACKEY_PCSC_E_LOCKED:
CACKEY_DEBUG_PRINTF("Token is locked or this change is not permitted.");
return(CKR_PIN_LOCKED);
default:
CACKEY_DEBUG_PRINTF("Something else went wrong changing the PIN: %i", set_pin_ret);
return(CKR_GENERAL_ERROR);
}
return(CKR_GENERAL_ERROR);
}
CK_DEFINE_FUNCTION(CK_RV, C_OpenSession)(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession) {
unsigned long idx;
int mutex_retval;
int found_session = 0;
CACKEY_DEBUG_PRINTF("Called.");
if ((flags & CKF_SERIAL_SESSION) != CKF_SERIAL_SESSION) {
return(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_SLOT_ID_INVALID);
}
/* Verify that the card is actually in the slot. */
/* XXX: Check to make sure this is in the PKCS#11 specification */
if (cackey_token_present(&cackey_slots[slotID]) != CACKEY_PCSC_S_TOKENPRESENT) {
CACKEY_DEBUG_PRINTF("Error. Card not present. Returning CKR_DEVICE_REMOVED");
cackey_mutex_unlock(cackey_biglock);
return(CKR_DEVICE_REMOVED);
}
for (idx = 1; idx < (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])); idx++) {
if (!cackey_sessions[idx].active) {
found_session = 1;
*phSession = idx;
cackey_sessions[idx].active = 1;
cackey_sessions[idx].slotID = slotID;
cackey_sessions[idx].state = CKS_RO_PUBLIC_SESSION;
cackey_sessions[idx].flags = flags;
cackey_sessions[idx].ulDeviceError = 0;
cackey_sessions[idx].pApplication = pApplication;
cackey_sessions[idx].Notify = notify;
cackey_sessions[idx].identities = NULL;
cackey_sessions[idx].identities_count = 0;
cackey_sessions[idx].search_active = 0;
cackey_sessions[idx].sign_active = 0;
cackey_sessions[idx].decrypt_active = 0;
cackey_sessions[idx].identities = cackey_read_identities(&cackey_slots[slotID], &cackey_sessions[idx].identities_count);
break;
}
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
if (!found_session) {
CACKEY_DEBUG_PRINTF("Returning CKR_SESSION_COUNT (%i)", CKR_SESSION_COUNT);
return(CKR_SESSION_COUNT);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_CloseSession)(CK_SESSION_HANDLE hSession) {
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
cackey_sessions[hSession].active = 0;
cackey_free_identities(cackey_sessions[hSession].identities, cackey_sessions[hSession].identities_count);
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_CloseAllSessions)(CK_SLOT_ID slotID) {
uint32_t idx;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_SLOT_ID_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_SLOT_ID_INVALID);
}
for (idx = 0; idx < (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])); idx++) {
if (cackey_sessions[idx].active) {
if (cackey_sessions[idx].slotID != slotID) {
continue;
}
cackey_mutex_unlock(cackey_biglock);
C_CloseSession(idx);
cackey_mutex_lock(cackey_biglock);
}
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetSessionInfo)(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) {
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (pInfo == NULL) {
CACKEY_DEBUG_PRINTF("Error. pInfo is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
pInfo->slotID = cackey_sessions[hSession].slotID;
pInfo->state = cackey_sessions[hSession].state;
pInfo->flags = cackey_sessions[hSession].flags;
pInfo->ulDeviceError = cackey_sessions[hSession].ulDeviceError;
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetOperationState)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SetOperationState)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_LoginMutexArg)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, int lock_mutex) {
CK_SLOT_ID slotID;
cackey_ret get_pin_ret;
char pinbuf[64];
int mutex_retval;
int tries_remaining;
int login_ret;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (userType != CKU_USER) {
CACKEY_DEBUG_PRINTF("Error. We only support USER mode, asked for %lu mode.", (unsigned long) userType)
return(CKR_USER_TYPE_INVALID);
}
if (lock_mutex) {
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
}
if (!cackey_sessions[hSession].active) {
if (lock_mutex) {
cackey_mutex_unlock(cackey_biglock);
}
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
if (lock_mutex) {
cackey_mutex_unlock(cackey_biglock);
}
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
if (lock_mutex) {
cackey_mutex_unlock(cackey_biglock);
}
return(CKR_GENERAL_ERROR);
}
if (cackey_pin_command != NULL) {
if (pPin != NULL) {
CACKEY_DEBUG_PRINTF("Protected authentication path in effect and PIN provided !?");
}
get_pin_ret = cackey_get_pin(pinbuf);
if (get_pin_ret != CACKEY_PCSC_S_OK) {
CACKEY_DEBUG_PRINTF("cackey_get_pin() returned in failure, assuming the PIN was incorrect.");
if (lock_mutex) {
cackey_mutex_unlock(cackey_biglock);
}
return(CKR_PIN_INCORRECT);
}
pPin = (CK_UTF8CHAR_PTR) pinbuf;
ulPinLen = strlen(pinbuf);
}
login_ret = cackey_login(&cackey_slots[slotID], pPin, ulPinLen, &tries_remaining, 3);
if (login_ret != CACKEY_PCSC_S_OK) {
if (lock_mutex) {
cackey_mutex_unlock(cackey_biglock);
}
if (login_ret == CACKEY_PCSC_E_LOCKED) {
CACKEY_DEBUG_PRINTF("Error. Token is locked.");
cackey_slots[slotID].token_flags |= CKF_USER_PIN_LOCKED;
CACKEY_DEBUG_PRINTF("Returning CKR_PIN_LOCKED (%i)", (int) CKR_PIN_LOCKED);
return(CKR_PIN_LOCKED);
} else if (login_ret == CACKEY_PCSC_E_BADPIN) {
CACKEY_DEBUG_PRINTF("Error. Invalid PIN.");
cackey_slots[slotID].token_flags |= CKF_USER_PIN_COUNT_LOW;
if (tries_remaining == 1) {
cackey_slots[slotID].token_flags |= CKF_USER_PIN_FINAL_TRY;
}
CACKEY_DEBUG_PRINTF("Returning CKR_PIN_INCORRECT (%i)", (int) CKR_PIN_INCORRECT);
return(CKR_PIN_INCORRECT);
}
CACKEY_DEBUG_PRINTF("Error. Unknown error returned from cackey_login() (%i)", login_ret);
return(CKR_GENERAL_ERROR);
}
cackey_slots[slotID].token_flags &= ~(CKF_USER_PIN_LOCKED | CKF_USER_PIN_COUNT_LOW | CKF_LOGIN_REQUIRED | CKF_USER_PIN_FINAL_TRY);
cackey_sessions[hSession].state = CKS_RO_USER_FUNCTIONS;
if (lock_mutex) {
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_Login)(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) {
return(C_LoginMutexArg(hSession, userType, pPin, ulPinLen, 1));
}
CK_DEFINE_FUNCTION(CK_RV, C_Logout)(CK_SESSION_HANDLE hSession) {
CK_SLOT_ID slotID;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
cackey_sessions[hSession].state = CKS_RO_PUBLIC_SESSION;
if (cackey_pin_command == NULL) {
cackey_slots[slotID].token_flags = CKF_LOGIN_REQUIRED;
} else {
cackey_slots[slotID].token_flags = 0;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_CopyObject)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetObjectSize)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GetAttributeValue)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
CK_ATTRIBUTE *curr_attr;
struct cackey_identity *identity;
unsigned long identity_idx, attr_idx, sess_attr_idx, num_ids;
int mutex_retval;
CK_RV retval = CKR_OK;
CK_VOID_PTR pValue;
CK_ULONG ulValueLen;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (hObject == 0) {
CACKEY_DEBUG_PRINTF("Error. Object handle out of range.");
return(CKR_OBJECT_HANDLE_INVALID);
}
if (ulCount == 0) {
/* Short circuit, if zero objects were specified return zero items immediately */
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i) (short circuit)", CKR_OK);
return(CKR_OK);
}
if (pTemplate == NULL) {
CACKEY_DEBUG_PRINTF("Error. pTemplate is NULL.");
return(CKR_ARGUMENTS_BAD);
}
identity_idx = hObject - 1;
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
num_ids = cackey_sessions[hSession].identities_count;
if (identity_idx >= num_ids) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Object handle out of range. identity_idx = %lu, num_ids = %lu.", (unsigned long) identity_idx, (unsigned long) num_ids);
return(CKR_OBJECT_HANDLE_INVALID);
}
identity = &cackey_sessions[hSession].identities[identity_idx];
for (attr_idx = 0; attr_idx < ulCount; attr_idx++) {
curr_attr = &pTemplate[attr_idx];
pValue = NULL;
ulValueLen = (CK_LONG) -1;
CACKEY_DEBUG_PRINTF("Looking for attribute 0x%08lx (identity:%lu) ...", (unsigned long) curr_attr->type, (unsigned long) identity_idx);
for (sess_attr_idx = 0; sess_attr_idx < identity->attributes_count; sess_attr_idx++) {
if (identity->attributes[sess_attr_idx].type == curr_attr->type) {
CACKEY_DEBUG_PRINTF(" ... found it, pValue = %p, ulValueLen = %lu", identity->attributes[sess_attr_idx].pValue, identity->attributes[sess_attr_idx].ulValueLen);
pValue = identity->attributes[sess_attr_idx].pValue;
ulValueLen = identity->attributes[sess_attr_idx].ulValueLen;
}
}
if (curr_attr->pValue && pValue) {
if (curr_attr->ulValueLen >= ulValueLen) {
memcpy(curr_attr->pValue, pValue, ulValueLen);
} else {
ulValueLen = (CK_LONG) -1;
retval = CKR_BUFFER_TOO_SMALL;
}
}
curr_attr->ulValueLen = ulValueLen;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
if (retval == CKR_ATTRIBUTE_TYPE_INVALID) {
CACKEY_DEBUG_PRINTF("Returning CKR_ATTRIBUTE_TYPE_INVALID (%i)", (int) retval);
} else if (retval == CKR_BUFFER_TOO_SMALL) {
CACKEY_DEBUG_PRINTF("Returning CKR_BUFFER_TOO_SMALL (%i)", (int) retval);
} else if (retval == CKR_OK) {
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", (int) retval);
} else {
CACKEY_DEBUG_PRINTF("Returning %i", (int) retval);
}
return(retval);
}
CK_DEFINE_FUNCTION(CK_RV, C_SetAttributeValue)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
CK_SLOT_ID slotID;
CK_ULONG idx;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (cackey_sessions[hSession].search_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Search already active.");
return(CKR_OPERATION_ACTIVE);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].slot_reset) {
CACKEY_DEBUG_PRINTF("The slot has been reset since we last looked for identities -- rescanning");
if (cackey_sessions[hSession].identities != NULL) {
cackey_free_identities(cackey_sessions[hSession].identities, cackey_sessions[hSession].identities_count);
cackey_sessions[hSession].identities = NULL;
cackey_sessions[hSession].identities_count = 0;
}
if (cackey_slots[slotID].label != NULL) {
free(cackey_slots[slotID].label);
cackey_slots[slotID].label = NULL;
}
cackey_mark_slot_reset(&cackey_slots[slotID]);
cackey_slots[slotID].slot_reset = 0;
}
if (cackey_sessions[hSession].identities == NULL) {
cackey_sessions[hSession].identities = cackey_read_identities(&cackey_slots[slotID], &cackey_sessions[hSession].identities_count);
}
if (pTemplate != NULL) {
if (ulCount != 0) {
cackey_sessions[hSession].search_query_count = ulCount;
cackey_sessions[hSession].search_query = malloc(ulCount * sizeof(*pTemplate));
memcpy(cackey_sessions[hSession].search_query, pTemplate, ulCount * sizeof(*pTemplate));
for (idx = 0; idx < ulCount; idx++) {
if (pTemplate[idx].ulValueLen == 0) {
cackey_sessions[hSession].search_query[idx].pValue = NULL;
continue;
}
cackey_sessions[hSession].search_query[idx].pValue = malloc(pTemplate[idx].ulValueLen);
if (cackey_sessions[hSession].search_query[idx].pValue) {
memcpy(cackey_sessions[hSession].search_query[idx].pValue, pTemplate[idx].pValue, pTemplate[idx].ulValueLen);
}
}
} else {
cackey_sessions[hSession].search_query_count = 0;
cackey_sessions[hSession].search_query = NULL;
}
} else {
if (ulCount != 0) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Search query specified as NULL, but number of query terms not specified as 0.");
return(CKR_ARGUMENTS_BAD);
}
cackey_sessions[hSession].search_query_count = 0;
cackey_sessions[hSession].search_query = NULL;
}
cackey_sessions[hSession].search_active = 1;
cackey_sessions[hSession].search_curr_id = 0;
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
static int cackey_pkcs11_compare_attributes(CK_ATTRIBUTE *a, CK_ATTRIBUTE *b) {
unsigned char *smallbuf, *largebuf;
size_t smallbuf_len, largebuf_len;
if (a->type != b->type) {
return(0);
}
CACKEY_DEBUG_PRINTF(" ... found matching type ...");
CACKEY_DEBUG_PRINTBUF(" ... our value:", a->pValue, a->ulValueLen);
if (b->pValue == NULL) {
CACKEY_DEBUG_PRINTF(" ... found wildcard match");
return(1);
}
if (a->pValue == NULL) {
return(0);
}
if (b->ulValueLen == a->ulValueLen && memcmp(a->pValue, b->pValue, b->ulValueLen) == 0) {
CACKEY_DEBUG_PRINTF(" ... found exact match");
return(1);
}
switch (a->type) {
case CKA_MODULUS:
if (a->ulValueLen == b->ulValueLen) {
break;
}
if (a->ulValueLen > b->ulValueLen) {
smallbuf = b->pValue;
smallbuf_len = b->ulValueLen;
largebuf = a->pValue;
largebuf_len = a->ulValueLen;
} else {
smallbuf = a->pValue;
smallbuf_len = a->ulValueLen;
largebuf = b->pValue;
largebuf_len = b->ulValueLen;
}
for (; largebuf_len != smallbuf_len; largebuf++,largebuf_len--) {
if (largebuf[0] != 0) {
break;
}
}
if (largebuf_len != smallbuf_len) {
break;
}
if (memcmp(largebuf, smallbuf, smallbuf_len) == 0) {
CACKEY_DEBUG_PRINTF(" ... found approximate match");
return(1);
}
break;
}
return(0);
}
CK_DEFINE_FUNCTION(CK_RV, C_FindObjects)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) {
struct cackey_identity *curr_id;
CK_ATTRIBUTE *curr_attr;
CK_ULONG curr_id_idx, curr_out_id_idx, curr_attr_idx, sess_attr_idx;
CK_ULONG matched_count, prev_matched_count;
int mutex_retval;
#ifdef CACKEY_DEBUG_SEARCH_SPEEDTEST
struct timeval start, end;
uint64_t start_int, end_int;
#endif
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pulObjectCount == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulObjectCount is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (phObject == NULL && ulMaxObjectCount == 0) {
/* Short circuit, if zero objects were specified return zero items immediately */
*pulObjectCount = 0;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i) (short circuit)", CKR_OK);
return(CKR_OK);
}
if (phObject == NULL) {
CACKEY_DEBUG_PRINTF("Error. phObject is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (ulMaxObjectCount == 0) {
CACKEY_DEBUG_PRINTF("Error. Maximum number of objects specified as zero.");
return(CKR_ARGUMENTS_BAD);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].search_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Search not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
#ifdef CACKEY_DEBUG_SEARCH_SPEEDTEST
gettimeofday(&start, NULL);
#endif
curr_out_id_idx = 0;
for (curr_id_idx = cackey_sessions[hSession].search_curr_id; curr_id_idx < cackey_sessions[hSession].identities_count && ulMaxObjectCount; curr_id_idx++) {
curr_id = &cackey_sessions[hSession].identities[curr_id_idx];
CACKEY_DEBUG_PRINTF("Processing identity:%lu", (unsigned long) curr_id_idx);
matched_count = 0;
for (curr_attr_idx = 0; curr_attr_idx < cackey_sessions[hSession].search_query_count; curr_attr_idx++) {
prev_matched_count = matched_count;
curr_attr = &cackey_sessions[hSession].search_query[curr_attr_idx];
CACKEY_DEBUG_PRINTF(" Checking for attribute %s (0x%08lx) in identity:%i...", CACKEY_DEBUG_FUNC_ATTRIBUTE_TO_STR(curr_attr->type), (unsigned long) curr_attr->type, (int) curr_id_idx);
CACKEY_DEBUG_PRINTBUF(" Value looking for:", curr_attr->pValue, curr_attr->ulValueLen);
for (sess_attr_idx = 0; sess_attr_idx < curr_id->attributes_count; sess_attr_idx++) {
if (cackey_pkcs11_compare_attributes(&curr_id->attributes[sess_attr_idx], curr_attr)) {
matched_count++;
break;
}
}
/* If the attribute could not be matched, do not try to match additional attributes */
if (prev_matched_count == matched_count) {
break;
}
}
if (matched_count == cackey_sessions[hSession].search_query_count) {
CACKEY_DEBUG_PRINTF(" ... All %i attributes checked for found, adding identity:%i to returned list", (int) cackey_sessions[hSession].search_query_count, (int) curr_id_idx);
phObject[curr_out_id_idx] = curr_id_idx + 1;
ulMaxObjectCount--;
curr_out_id_idx++;
} else {
CACKEY_DEBUG_PRINTF(" ... Not all %i (only found %i) attributes checked for found, not adding identity:%i", (int) cackey_sessions[hSession].search_query_count, (int) matched_count, (int) curr_id_idx);
}
}
cackey_sessions[hSession].search_curr_id = curr_id_idx;
*pulObjectCount = curr_out_id_idx;
#ifdef CACKEY_DEBUG_SEARCH_SPEEDTEST
gettimeofday(&end, NULL);
start_int = (start.tv_sec * 1000000) + start.tv_usec;
end_int = (end.tv_sec * 1000000) + end.tv_usec;
fprintf(stderr, "Search took %lu microseconds\n", (unsigned long) (end_int - start_int));
#endif
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i), num objects = %lu", CKR_OK, *pulObjectCount);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsFinal)(CK_SESSION_HANDLE hSession) {
CK_ULONG idx;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].search_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Search not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
cackey_sessions[hSession].search_active = 0;
for (idx = 0; idx < cackey_sessions[hSession].search_query_count; idx++) {
if (cackey_sessions[hSession].search_query[idx].pValue) {
free(cackey_sessions[hSession].search_query[idx].pValue);
}
}
if (cackey_sessions[hSession].search_query) {
free(cackey_sessions[hSession].search_query);
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_EncryptInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_Encrypt)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_EncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_EncryptFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
int mutex_retval;
hKey--;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pMechanism == NULL) {
CACKEY_DEBUG_PRINTF("Error. pMechanism is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (pMechanism->mechanism != CKM_RSA_PKCS) {
CACKEY_DEBUG_PRINTF("Error. pMechanism->mechanism not specified as CKM_RSA_PKCS");
return(CKR_MECHANISM_PARAM_INVALID);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (cackey_sessions[hSession].decrypt_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Decrypt already in progress.");
return(CKR_OPERATION_ACTIVE);
}
if (hKey >= cackey_sessions[hSession].identities_count) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Key handle out of range (requested key %lu, only %lu identities available).", (unsigned long) hKey, (unsigned long) cackey_sessions[hSession].identities_count);
return(CKR_KEY_HANDLE_INVALID);
}
cackey_sessions[hSession].decrypt_active = 1;
cackey_sessions[hSession].decrypt_mechanism = pMechanism->mechanism;
cackey_sessions[hSession].decrypt_mech_parm = pMechanism->pParameter;
cackey_sessions[hSession].decrypt_mech_parmlen = pMechanism->ulParameterLen;
cackey_sessions[hSession].decrypt_identity = &cackey_sessions[hSession].identities[hKey];
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_Decrypt)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) {
CK_ULONG datalen_update, datalen_final;
CK_RV decrypt_ret;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pulDataLen == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulDataLen is NULL.");
return(CKR_ARGUMENTS_BAD);
}
datalen_update = *pulDataLen;
decrypt_ret = C_DecryptUpdate(hSession, pEncryptedData, ulEncryptedDataLen, pData, &datalen_update);
if (decrypt_ret != CKR_OK) {
CACKEY_DEBUG_PRINTF("Error. DecryptUpdate() returned failure (rv = %lu).", (unsigned long) decrypt_ret);
if (decrypt_ret != CKR_BUFFER_TOO_SMALL) {
/* Terminate decryption operation */
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].decrypt_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Decrypt not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
cackey_sessions[hSession].decrypt_active = 0;
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
}
return(decrypt_ret);
}
if (pData) {
pData += datalen_update;
}
datalen_final = *pulDataLen - datalen_update;
decrypt_ret = C_DecryptFinal(hSession, pData, &datalen_final);
if (decrypt_ret != CKR_OK) {
CACKEY_DEBUG_PRINTF("Error. DecryptFinal() returned failure (rv = %lu).", (unsigned long) decrypt_ret);
return(decrypt_ret);
}
*pulDataLen = datalen_update + datalen_final;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_DecryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
static CK_BYTE buf[16384];
ssize_t buflen;
CK_SLOT_ID slotID;
CK_RV retval = CKR_GENERAL_ERROR;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (pEncryptedPart == NULL && ulEncryptedPartLen == 0) {
/* Short circuit if we are asked to decrypt nothing... */
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i) (short circuit)", CKR_OK);
return(CKR_OK);
}
if (pEncryptedPart == NULL) {
CACKEY_DEBUG_PRINTF("Error. pEncryptedPart is NULL, but ulEncryptedPartLen is not 0.");
return(CKR_ARGUMENTS_BAD);
}
if (ulEncryptedPartLen == 0) {
CACKEY_DEBUG_PRINTF("Error. ulEncryptedPartLen is 0, but pPart is not NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (pulPartLen == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulPartLen is NULL.");
return(CKR_ARGUMENTS_BAD);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].decrypt_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Decrypt not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
switch (cackey_sessions[hSession].decrypt_mechanism) {
case CKM_RSA_PKCS:
/* Ask card to decrypt */
buflen = cackey_signdecrypt(&cackey_slots[slotID], cackey_sessions[hSession].decrypt_identity, pEncryptedPart, ulEncryptedPartLen, buf, sizeof(buf), 0, 1);
if (buflen == CACKEY_PCSC_E_NEEDLOGIN && cackey_pin_command != NULL) {
if (C_LoginMutexArg(hSession, CKU_USER, NULL, 0, 0) == CKR_OK) {
buflen = cackey_signdecrypt(&cackey_slots[slotID], cackey_sessions[hSession].decrypt_identity, pEncryptedPart, ulEncryptedPartLen, buf, sizeof(buf), 0, 1);
}
}
if (buflen < 0) {
/* Decryption failed. */
if (buflen == CACKEY_PCSC_E_NEEDLOGIN) {
retval = CKR_USER_NOT_LOGGED_IN;
} else if (buflen == CACKEY_PCSC_E_TOKENABSENT) {
retval = CKR_DEVICE_REMOVED;
} else {
CACKEY_DEBUG_PRINTF("Failed to send APDU, error = %li", (long int) buflen);
retval = CKR_GENERAL_ERROR;
}
} else if (((unsigned long) buflen) > *pulPartLen && pPart) {
/* Decrypted data too large */
retval = CKR_BUFFER_TOO_SMALL;
} else {
if (pPart) {
memcpy(pPart, buf, buflen);
}
*pulPartLen = buflen;
retval = CKR_OK;
}
break;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning %i", (int) retval);
return(retval);
}
CK_DEFINE_FUNCTION(CK_RV, C_DecryptFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) {
int mutex_retval;
int terminate_decrypt = 1;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (pulLastPartLen == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulLastPartLen is NULL.");
return(CKR_ARGUMENTS_BAD);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].decrypt_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Decrypt not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
*pulLastPartLen = 0;
if (pLastPart == NULL) {
terminate_decrypt = 0;
}
if (terminate_decrypt) {
cackey_sessions[hSession].decrypt_active = 0;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_DigestInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_Digest)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DigestUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DigestKey)(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DigestFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
int mutex_retval;
hKey--;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pMechanism == NULL) {
CACKEY_DEBUG_PRINTF("Error. pMechanism is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (pMechanism->mechanism != CKM_RSA_PKCS) {
CACKEY_DEBUG_PRINTF("Error. pMechanism->mechanism not specified as CKM_RSA_PKCS");
return(CKR_MECHANISM_PARAM_INVALID);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (cackey_sessions[hSession].sign_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Sign already in progress.");
return(CKR_OPERATION_ACTIVE);
}
if (hKey >= cackey_sessions[hSession].identities_count) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Key handle out of range (requested key %lu, only %lu identities available).", (unsigned long) hKey, (unsigned long) cackey_sessions[hSession].identities_count);
return(CKR_KEY_HANDLE_INVALID);
}
cackey_sessions[hSession].sign_active = 1;
cackey_sessions[hSession].sign_mechanism = pMechanism->mechanism;
cackey_sessions[hSession].sign_buflen = 128;
cackey_sessions[hSession].sign_bufused = 0;
cackey_sessions[hSession].sign_buf = malloc(sizeof(*cackey_sessions[hSession].sign_buf) * cackey_sessions[hSession].sign_buflen);
CACKEY_DEBUG_PRINTF("Session %lu sign_identity is %p (identity #%lu)", (unsigned long) hSession, (void *) &cackey_sessions[hSession].identities[hKey], (unsigned long) hKey);
cackey_sessions[hSession].sign_identity = &cackey_sessions[hSession].identities[hKey];
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_Sign)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
unsigned long start_sign_bufused;
CK_RV sign_ret;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
start_sign_bufused = cackey_sessions[hSession].sign_bufused;
sign_ret = C_SignUpdate(hSession, pData, ulDataLen);
if (sign_ret != CKR_OK) {
CACKEY_DEBUG_PRINTF("Error. SignUpdate() returned failure (rv = %lu).", (unsigned long) sign_ret);
if (sign_ret != CKR_BUFFER_TOO_SMALL) {
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].sign_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Sign not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
cackey_sessions[hSession].sign_active = 0;
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
}
return(sign_ret);
}
sign_ret = C_SignFinal(hSession, pSignature, pulSignatureLen);
if (sign_ret != CKR_OK) {
if (sign_ret == CKR_BUFFER_TOO_SMALL) {
CACKEY_DEBUG_PRINTF("SignFinal() returned CKR_BUFFER_TOO_SMALL (rv = %lu), undoing C_SignUpdate()", (unsigned long) sign_ret);
cackey_sessions[hSession].sign_bufused = start_sign_bufused;
return(sign_ret);
}
CACKEY_DEBUG_PRINTF("Error. SignFinal() returned failure (rv = %lu).", (unsigned long) sign_ret);
return(sign_ret);
}
if (pSignature == NULL) {
CACKEY_DEBUG_PRINTF("pSignature specified as NULL, undoing C_SignUpdate()");
cackey_sessions[hSession].sign_bufused = start_sign_bufused;
return(sign_ret);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
int mutex_retval;
int resizeRetry;
int needResize;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (pPart == NULL && ulPartLen == 0) {
/* Short circuit if we are asked to sign nothing... */
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i) (short circuit)", CKR_OK);
return(CKR_OK);
}
if (pPart == NULL) {
CACKEY_DEBUG_PRINTF("Error. pPart is NULL, but ulPartLen is not 0.");
return(CKR_ARGUMENTS_BAD);
}
if (ulPartLen == 0) {
CACKEY_DEBUG_PRINTF("Error. ulPartLen is 0, but pPart is not NULL.");
return(CKR_ARGUMENTS_BAD);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].sign_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Sign not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
switch (cackey_sessions[hSession].sign_mechanism) {
case CKM_RSA_PKCS:
/* Accumulate directly */
for (resizeRetry = 0; resizeRetry < 11; resizeRetry++) {
needResize = 0;
if ((cackey_sessions[hSession].sign_bufused + ulPartLen) > cackey_sessions[hSession].sign_buflen) {
needResize = 1;
}
if (!needResize) {
break;
}
CACKEY_DEBUG_PRINTF("Resizing signing buffer (try #%i of 10 -- 11th is fatal)", resizeRetry);
if (resizeRetry == 10) {
free(cackey_sessions[hSession].sign_buf);
cackey_sessions[hSession].sign_buflen = 0;
cackey_sessions[hSession].sign_buf = NULL;
break;
}
cackey_sessions[hSession].sign_buflen *= 2;
cackey_sessions[hSession].sign_buf = realloc(cackey_sessions[hSession].sign_buf, sizeof(*cackey_sessions[hSession].sign_buf) * cackey_sessions[hSession].sign_buflen);
}
if (cackey_sessions[hSession].sign_buf == NULL) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Signing buffer is NULL.");
return(CKR_GENERAL_ERROR);
}
memcpy(cackey_sessions[hSession].sign_buf + cackey_sessions[hSession].sign_bufused, pPart, ulPartLen);
cackey_sessions[hSession].sign_bufused += ulPartLen;
break;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
static CK_BYTE sigbuf[1024];
ssize_t sigbuflen;
CK_SLOT_ID slotID;
CK_RV retval = CKR_GENERAL_ERROR;
int terminate_sign = 1;
int mutex_retval;
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pulSignatureLen == NULL) {
CACKEY_DEBUG_PRINTF("Error. pulSignatureLen is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (hSession == 0 || hSession >= (sizeof(cackey_sessions) / sizeof(cackey_sessions[0]))) {
CACKEY_DEBUG_PRINTF("Error. Session out of range.");
return(CKR_SESSION_HANDLE_INVALID);
}
mutex_retval = cackey_mutex_lock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Locking failed.");
return(CKR_GENERAL_ERROR);
}
if (!cackey_sessions[hSession].active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Session not active.");
return(CKR_SESSION_HANDLE_INVALID);
}
if (!cackey_sessions[hSession].sign_active) {
cackey_mutex_unlock(cackey_biglock);
CACKEY_DEBUG_PRINTF("Error. Sign not active.");
return(CKR_OPERATION_NOT_INITIALIZED);
}
slotID = cackey_sessions[hSession].slotID;
if (slotID < 0 || slotID >= (sizeof(cackey_slots) / sizeof(cackey_slots[0]))) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), outside of valid range", slotID);
return(CKR_GENERAL_ERROR);
}
if (cackey_slots[slotID].active == 0) {
CACKEY_DEBUG_PRINTF("Error. Invalid slot requested (%lu), slot not currently active", slotID);
cackey_mutex_unlock(cackey_biglock);
return(CKR_GENERAL_ERROR);
}
switch (cackey_sessions[hSession].sign_mechanism) {
case CKM_RSA_PKCS:
/* Ask card to sign */
CACKEY_DEBUG_PRINTF("Asking to sign from identity %p in session %lu", (void *) cackey_sessions[hSession].sign_identity, (unsigned long) hSession);
sigbuflen = cackey_signdecrypt(&cackey_slots[slotID], cackey_sessions[hSession].sign_identity, cackey_sessions[hSession].sign_buf, cackey_sessions[hSession].sign_bufused, sigbuf, sizeof(sigbuf), 1, 0);
if (sigbuflen == CACKEY_PCSC_E_NEEDLOGIN && cackey_pin_command != NULL) {
if (C_LoginMutexArg(hSession, CKU_USER, NULL, 0, 0) == CKR_OK) {
sigbuflen = cackey_signdecrypt(&cackey_slots[slotID], cackey_sessions[hSession].sign_identity, cackey_sessions[hSession].sign_buf, cackey_sessions[hSession].sign_bufused, sigbuf, sizeof(sigbuf), 1, 0);
}
}
if (sigbuflen < 0) {
/* Signing failed. */
if (sigbuflen == CACKEY_PCSC_E_NEEDLOGIN) {
retval = CKR_USER_NOT_LOGGED_IN;
} else if (sigbuflen == CACKEY_PCSC_E_TOKENABSENT) {
retval = CKR_DEVICE_REMOVED;
} else {
retval = CKR_GENERAL_ERROR;
}
} else if (((unsigned long) sigbuflen) > *pulSignatureLen && pSignature) {
/* Signed data too large */
CACKEY_DEBUG_PRINTF("retval = CKR_BUFFER_TOO_SMALL; sigbuflen = %lu, pulSignatureLen = %lu", (unsigned long) sigbuflen, (unsigned long) *pulSignatureLen);
retval = CKR_BUFFER_TOO_SMALL;
terminate_sign = 0;
} else {
terminate_sign = 0;
if (pSignature) {
memcpy(pSignature, sigbuf, sigbuflen);
terminate_sign = 1;
}
*pulSignatureLen = sigbuflen;
retval = CKR_OK;
}
break;
}
if (terminate_sign) {
if (cackey_sessions[hSession].sign_buf) {
free(cackey_sessions[hSession].sign_buf);
}
cackey_sessions[hSession].sign_active = 0;
}
mutex_retval = cackey_mutex_unlock(cackey_biglock);
if (mutex_retval != 0) {
CACKEY_DEBUG_PRINTF("Error. Unlocking failed.");
return(CKR_GENERAL_ERROR);
}
CACKEY_DEBUG_PRINTF("Returning %i", (int) retval);
return(retval);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignRecoverInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignRecover)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_VerifyInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_Verify)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_VerifyUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_VerifyFinal)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecoverInit)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_VerifyRecover)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DigestEncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DecryptDigestUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SignEncryptUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DecryptVerifyUpdate)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GenerateKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_WrapKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_DeriveKey)(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_SeedRandom)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
CK_DEFINE_FUNCTION(CK_RV, C_GenerateRandom)(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) {
CACKEY_DEBUG_PRINTF("Called.");
if (!cackey_initialized) {
CACKEY_DEBUG_PRINTF("Error. Not initialized.");
return(CKR_CRYPTOKI_NOT_INITIALIZED);
}
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED);
return(CKR_FUNCTION_NOT_SUPPORTED);
}
/* Deprecated Function */
CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionStatus)(CK_SESSION_HANDLE hSession) {
CACKEY_DEBUG_PRINTF("Called.");
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_PARALLEL (%i)", CKR_FUNCTION_NOT_PARALLEL);
return(CKR_FUNCTION_NOT_PARALLEL);
hSession = hSession; /* Supress unused variable warning */
}
/* Deprecated Function */
CK_DEFINE_FUNCTION(CK_RV, C_CancelFunction)(CK_SESSION_HANDLE hSession) {
CACKEY_DEBUG_PRINTF("Called.");
CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_PARALLEL (%i)", CKR_FUNCTION_NOT_PARALLEL);
return(CKR_FUNCTION_NOT_PARALLEL);
hSession = hSession; /* Supress unused variable warning */
}
CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
static CK_FUNCTION_LIST_PTR spFunctionList = NULL;
CK_FUNCTION_LIST_PTR pFunctionList;
CACKEY_DEBUG_PRINTF("Called.");
if (ppFunctionList == NULL) {
CACKEY_DEBUG_PRINTF("Error. ppFunctionList is NULL.");
return(CKR_ARGUMENTS_BAD);
}
if (spFunctionList != NULL) {
*ppFunctionList = spFunctionList;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}
pFunctionList = malloc(sizeof(*pFunctionList));
pFunctionList->version.major = ((CACKEY_CRYPTOKI_VERSION_CODE) >> 16) & 0xff;
pFunctionList->version.minor = ((CACKEY_CRYPTOKI_VERSION_CODE) >> 8) & 0xff;
pFunctionList->C_Initialize = C_Initialize;
pFunctionList->C_Finalize = C_Finalize;
pFunctionList->C_GetInfo = C_GetInfo;
pFunctionList->C_GetSlotList = C_GetSlotList;
pFunctionList->C_GetSlotInfo = C_GetSlotInfo;
pFunctionList->C_GetTokenInfo = C_GetTokenInfo;
pFunctionList->C_WaitForSlotEvent = C_WaitForSlotEvent;
pFunctionList->C_GetMechanismList = C_GetMechanismList;
pFunctionList->C_GetMechanismInfo = C_GetMechanismInfo;
pFunctionList->C_InitToken = C_InitToken;
pFunctionList->C_InitPIN = C_InitPIN;
pFunctionList->C_SetPIN = C_SetPIN;
pFunctionList->C_OpenSession = C_OpenSession;
pFunctionList->C_CloseSession = C_CloseSession;
pFunctionList->C_CloseAllSessions = C_CloseAllSessions;
pFunctionList->C_GetSessionInfo = C_GetSessionInfo;
pFunctionList->C_GetOperationState = C_GetOperationState;
pFunctionList->C_SetOperationState = C_SetOperationState;
pFunctionList->C_Login = C_Login;
pFunctionList->C_Logout = C_Logout;
pFunctionList->C_CreateObject = C_CreateObject;
pFunctionList->C_CopyObject = C_CopyObject;
pFunctionList->C_DestroyObject = C_DestroyObject;
pFunctionList->C_GetObjectSize = C_GetObjectSize;
pFunctionList->C_GetAttributeValue = C_GetAttributeValue;
pFunctionList->C_SetAttributeValue = C_SetAttributeValue;
pFunctionList->C_FindObjectsInit = C_FindObjectsInit;
pFunctionList->C_FindObjects = C_FindObjects;
pFunctionList->C_FindObjectsFinal = C_FindObjectsFinal;
pFunctionList->C_EncryptInit = C_EncryptInit;
pFunctionList->C_Encrypt = C_Encrypt;
pFunctionList->C_EncryptUpdate = C_EncryptUpdate;
pFunctionList->C_EncryptFinal = C_EncryptFinal;
pFunctionList->C_DecryptInit = C_DecryptInit;
pFunctionList->C_Decrypt = C_Decrypt;
pFunctionList->C_DecryptUpdate = C_DecryptUpdate;
pFunctionList->C_DecryptFinal = C_DecryptFinal;
pFunctionList->C_DigestInit = C_DigestInit;
pFunctionList->C_Digest = C_Digest;
pFunctionList->C_DigestUpdate = C_DigestUpdate;
pFunctionList->C_DigestKey = C_DigestKey;
pFunctionList->C_DigestFinal = C_DigestFinal;
pFunctionList->C_SignInit = C_SignInit;
pFunctionList->C_Sign = C_Sign;
pFunctionList->C_SignUpdate = C_SignUpdate;
pFunctionList->C_SignFinal = C_SignFinal;
pFunctionList->C_SignRecoverInit = C_SignRecoverInit;
pFunctionList->C_SignRecover = C_SignRecover;
pFunctionList->C_VerifyInit = C_VerifyInit;
pFunctionList->C_Verify = C_Verify;
pFunctionList->C_VerifyUpdate = C_VerifyUpdate;
pFunctionList->C_VerifyFinal = C_VerifyFinal;
pFunctionList->C_VerifyRecoverInit = C_VerifyRecoverInit;
pFunctionList->C_VerifyRecover = C_VerifyRecover;
pFunctionList->C_DigestEncryptUpdate = C_DigestEncryptUpdate;
pFunctionList->C_DecryptDigestUpdate = C_DecryptDigestUpdate;
pFunctionList->C_SignEncryptUpdate = C_SignEncryptUpdate;
pFunctionList->C_DecryptVerifyUpdate = C_DecryptVerifyUpdate;
pFunctionList->C_GenerateKey = C_GenerateKey;
pFunctionList->C_GenerateKeyPair = C_GenerateKeyPair;
pFunctionList->C_WrapKey = C_WrapKey;
pFunctionList->C_UnwrapKey = C_UnwrapKey;
pFunctionList->C_DeriveKey = C_DeriveKey;
pFunctionList->C_SeedRandom = C_SeedRandom;
pFunctionList->C_GenerateRandom = C_GenerateRandom;
pFunctionList->C_GetFunctionStatus = C_GetFunctionStatus;
pFunctionList->C_CancelFunction = C_CancelFunction;
pFunctionList->C_GetFunctionList = C_GetFunctionList;
spFunctionList = pFunctionList;
*ppFunctionList = pFunctionList;
CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK);
return(CKR_OK);
}