[7d3ae3e] | 1 | /* |
---|
| 2 | * Estonian ID card plugin for web browsers |
---|
| 3 | * |
---|
| 4 | * Copyright (C) 2010-2011 Codeborne <info@codeborne.com> |
---|
| 5 | * |
---|
| 6 | * This is free software; you can redistribute it and/or |
---|
| 7 | * modify it under the terms of the GNU Lesser General Public |
---|
| 8 | * License as published by the Free Software Foundation; either |
---|
| 9 | * version 2.1 of the License, or (at your option) any later version. |
---|
| 10 | * |
---|
| 11 | * This software is distributed in the hope that it will be useful, |
---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
| 14 | * Lesser General Public License for more details. |
---|
| 15 | * |
---|
| 16 | * You should have received a copy of the GNU Lesser General Public |
---|
| 17 | * License along with this library; if not, write to the Free Software |
---|
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
---|
| 19 | * |
---|
| 20 | */ |
---|
| 21 | |
---|
| 22 | #include <stdlib.h> |
---|
| 23 | #include <stdio.h> |
---|
| 24 | #include <string.h> |
---|
| 25 | |
---|
| 26 | #ifndef _WIN32 |
---|
| 27 | #include <unistd.h> |
---|
| 28 | #include <pthread.h> |
---|
| 29 | #endif |
---|
| 30 | |
---|
| 31 | #include <openssl/x509.h> |
---|
| 32 | #include <openssl/pem.h> |
---|
| 33 | #include <openssl/md5.h> |
---|
| 34 | |
---|
| 35 | #include "esteid_certinfo.h" |
---|
| 36 | #include "esteid_log.h" |
---|
| 37 | #include "esteid_timer.h" |
---|
| 38 | #include "pkcs11_errors.h" |
---|
| 39 | #include "esteid_map.h" |
---|
| 40 | #include "esteid_error.h" |
---|
| 41 | |
---|
| 42 | #ifdef _WIN32 |
---|
| 43 | #include <windows.h> |
---|
| 44 | #define LOAD_LIBRARY HMODULE handle = LoadLibrary(L"opensc-pkcs11.dll") |
---|
| 45 | #define GET_FUNCTION_PTR GetProcAddress |
---|
| 46 | char error_buffer[32]; |
---|
| 47 | |
---|
| 48 | char *library_error() { |
---|
| 49 | sprintf(error_buffer, "code %lu", GetLastError()); |
---|
| 50 | return error_buffer; |
---|
| 51 | } |
---|
| 52 | #else |
---|
| 53 | #include <dlfcn.h> |
---|
| 54 | #ifdef __APPLE__ |
---|
| 55 | #define LOAD_LIBRARY void *handle = dlopen("/Library/EstonianIDCard/lib/esteid-pkcs11.so", RTLD_NOW) |
---|
| 56 | #else |
---|
| 57 | #define LOAD_LIBRARY void *handle = dlopen("opensc-pkcs11.so", RTLD_NOW) |
---|
| 58 | #endif |
---|
| 59 | #define GET_FUNCTION_PTR dlsym |
---|
| 60 | char *library_error() { |
---|
| 61 | return dlerror(); |
---|
| 62 | } |
---|
| 63 | #endif |
---|
| 64 | |
---|
| 65 | // on Windows it is important that openssl is included _after_ windows.h |
---|
| 66 | #include <openssl/x509.h> |
---|
| 67 | #include <openssl/pem.h> |
---|
| 68 | |
---|
| 69 | #define FAILURE 0 |
---|
| 70 | #define SUCCESS 1 |
---|
| 71 | |
---|
| 72 | #define FAIL_IF(failure) if (failure) { EstEID_freeCerts(); return FAILURE; } |
---|
| 73 | #define FAIL_UNLESS(success) if (!success) { EstEID_freeCerts(); return FAILURE; } |
---|
| 74 | |
---|
| 75 | CK_FUNCTION_LIST_PTR fl = NULL; |
---|
| 76 | |
---|
| 77 | static EstEID_Certs *certs = NULL; |
---|
| 78 | |
---|
| 79 | char EstEID_error[ESTEID_ERROR_SIZE]; |
---|
| 80 | int EstEID_errorCode; |
---|
| 81 | |
---|
| 82 | void EstEID_clear_error(void) { |
---|
| 83 | EstEID_error[0] = 0; |
---|
| 84 | EstEID_errorCode = ESTEID_NO_ERROR; |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | int EstEID_CK_failure(const char *name, CK_RV result) { |
---|
| 88 | EstEID_clear_error(); |
---|
| 89 | if (result == CKR_OK || result == CKR_CRYPTOKI_ALREADY_INITIALIZED) return FAILURE; |
---|
| 90 | sprintf(EstEID_error, "%s error: %s (%li)", name, pkcs11_error_message(result), result); |
---|
| 91 | EstEID_errorCode = ESTEID_PKCS11_ERROR; |
---|
| 92 | EstEID_log("cryptoki error: %s", EstEID_error); |
---|
| 93 | return SUCCESS; |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | int EstEID_dl_failure(const char *name, void *ptr) { |
---|
| 97 | EstEID_clear_error(); |
---|
| 98 | if (ptr) return FALSE; |
---|
| 99 | snprintf(EstEID_error, sizeof(EstEID_error) - 1, "%s failed: %s", name, library_error()); |
---|
| 100 | EstEID_errorCode = ESTEID_LIBRARY_LOAD_ERROR; |
---|
| 101 | EstEID_error[sizeof(EstEID_error) - 1] = 0; |
---|
| 102 | EstEID_log("dl error: %s", EstEID_error); |
---|
| 103 | return TRUE; |
---|
| 104 | } |
---|
| 105 | |
---|
| 106 | int EstEID_md5_failure(void *ptr) { |
---|
| 107 | EstEID_clear_error(); |
---|
| 108 | if (ptr) return FALSE; |
---|
| 109 | snprintf(EstEID_error, sizeof(EstEID_error) - 1, "Cert id creation failed"); |
---|
| 110 | EstEID_errorCode = ESTEID_MD5_ERROR; |
---|
| 111 | EstEID_error[sizeof(EstEID_error) - 1] = 0; |
---|
| 112 | EstEID_log("%s", EstEID_error); |
---|
| 113 | return TRUE; |
---|
| 114 | } |
---|
| 115 | |
---|
| 116 | char *EstEID_createString(CK_UTF8CHAR *padded, int length) { |
---|
| 117 | int i = 0; |
---|
| 118 | char *result = (char *)malloc(sizeof(char)* (length + 1)); |
---|
| 119 | |
---|
| 120 | strncpy(result, (char *)padded, length); |
---|
| 121 | result[length] = ' '; |
---|
| 122 | for (i = length; i >= 0 && result[i] == ' '; i--) result[i] = 0; |
---|
| 123 | return result; |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | void EstEID_logInitInfo(CK_INFO info) { |
---|
| 127 | char *libraryDescription = EstEID_createString(info.libraryDescription, 32); |
---|
| 128 | char *manufacturerID = EstEID_createString(info.manufacturerID, 32); |
---|
| 129 | EstEID_log("cryptoki %i.%i, library %i.%i %s, %s", info.cryptokiVersion.major, info.cryptokiVersion.minor, info.libraryVersion.major, info.libraryVersion.minor, libraryDescription, manufacturerID); |
---|
| 130 | free(libraryDescription); |
---|
| 131 | free(manufacturerID); |
---|
| 132 | } |
---|
| 133 | |
---|
| 134 | #ifdef _WIN32 |
---|
| 135 | #define pthread_mutex_t HANDLE |
---|
| 136 | #define pthread_cond_t HANDLE |
---|
| 137 | #endif |
---|
| 138 | |
---|
| 139 | pthread_mutex_t initialization_mutex; |
---|
| 140 | pthread_cond_t initialization_condition; |
---|
| 141 | //pthread_t initialization_thread; |
---|
| 142 | int initialization_result; |
---|
| 143 | int initialization_completed = FALSE; |
---|
| 144 | |
---|
| 145 | THREAD_RETURN_TYPE EstEID_initializeCryptokiThread(void *v) { |
---|
| 146 | CK_C_INITIALIZE_ARGS init_args; |
---|
| 147 | struct timeval start; |
---|
| 148 | |
---|
| 149 | LOG_LOCATION; |
---|
| 150 | #ifdef _WIN32 |
---|
| 151 | WaitForSingleObject(initialization_mutex, INFINITE); |
---|
| 152 | #else |
---|
| 153 | pthread_mutex_lock(&initialization_mutex); |
---|
| 154 | #endif |
---|
| 155 | memset( &init_args, 0x0, sizeof(init_args) ); |
---|
| 156 | init_args.flags = CKF_OS_LOCKING_OK; |
---|
| 157 | |
---|
| 158 | start = EstEID_startTimer(); |
---|
| 159 | initialization_result = fl->C_Initialize(&init_args); |
---|
| 160 | EstEID_log("C_Initialize: %s (%li)", pkcs11_error_message(initialization_result), initialization_result); |
---|
| 161 | EstEID_stopTimerAndLog(start, "C_Initialize:"); |
---|
| 162 | |
---|
| 163 | if (initialization_result == CKR_CRYPTOKI_ALREADY_INITIALIZED) { |
---|
| 164 | EstEID_log("initialization_result == CKR_CRYPTOKI_ALREADY_INITIALIZED"); |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | if (initialization_result == CKR_OK) { |
---|
| 168 | CK_INFO info; |
---|
| 169 | EstEID_log("initialization_result == CKR_OK"); |
---|
| 170 | if (!EstEID_CK_failure("C_GetInfo", fl->C_GetInfo(&info))) { |
---|
| 171 | EstEID_logInitInfo(info); |
---|
| 172 | }; |
---|
| 173 | } |
---|
| 174 | initialization_completed = TRUE; |
---|
| 175 | #ifdef _WIN32 |
---|
| 176 | ReleaseMutex(initialization_mutex); |
---|
| 177 | return TRUE; |
---|
| 178 | #else |
---|
| 179 | pthread_cond_broadcast(&initialization_condition); |
---|
| 180 | pthread_mutex_unlock(&initialization_mutex); |
---|
| 181 | pthread_exit(NULL); |
---|
| 182 | #endif |
---|
| 183 | } |
---|
| 184 | |
---|
| 185 | #ifdef _WIN32 |
---|
| 186 | |
---|
| 187 | int EstEID_startInitializeCryptokiThread() { |
---|
| 188 | DWORD threadId; |
---|
| 189 | |
---|
| 190 | LOG_LOCATION; |
---|
| 191 | |
---|
| 192 | initialization_result = -1; |
---|
| 193 | FAIL_IF_THREAD_ERROR("CreateMutex", (initialization_mutex = CreateMutex(NULL, FALSE, NULL))); |
---|
| 194 | EstEID_log("initialization_mutex = %p", initialization_mutex); |
---|
| 195 | FAIL_IF_THREAD_ERROR("CreateThread", CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&EstEID_initializeCryptokiThread, NULL, 0, &threadId)); |
---|
| 196 | |
---|
| 197 | return SUCCESS; |
---|
| 198 | } |
---|
| 199 | |
---|
| 200 | #else |
---|
| 201 | |
---|
| 202 | int EstEID_startInitializeCryptokiThread() { |
---|
| 203 | initialization_result = -1; |
---|
| 204 | FAIL_IF_PTHREAD_ERROR("pthread_mutex_init", pthread_mutex_init(&initialization_mutex, NULL)); |
---|
| 205 | FAIL_IF_PTHREAD_ERROR("pthread_cond_init", pthread_cond_init(&initialization_condition, NULL)); |
---|
| 206 | pthread_t initialization_thread; |
---|
| 207 | FAIL_IF_PTHREAD_ERROR("pthread_create", pthread_create(&initialization_thread, NULL, EstEID_initializeCryptokiThread, NULL)); |
---|
| 208 | return SUCCESS; |
---|
| 209 | } |
---|
| 210 | |
---|
| 211 | #endif |
---|
| 212 | |
---|
| 213 | int EstEID_loadLibrary() { |
---|
| 214 | CK_C_GetFunctionList GetFunctionList; |
---|
| 215 | LOAD_LIBRARY; |
---|
| 216 | |
---|
| 217 | LOG_LOCATION; |
---|
| 218 | if (fl) return SUCCESS; |
---|
| 219 | |
---|
| 220 | if (EstEID_dl_failure("dlopen", handle)) return FAILURE; |
---|
| 221 | GetFunctionList = (CK_C_GetFunctionList)GET_FUNCTION_PTR(handle, "C_GetFunctionList"); |
---|
| 222 | if (EstEID_dl_failure("dlsym", GetFunctionList)) return FAILURE; |
---|
| 223 | if (EstEID_CK_failure("GetFunctionList", GetFunctionList(&fl))) return FAILURE; |
---|
| 224 | if (!EstEID_startInitializeCryptokiThread()) return FAILURE; |
---|
| 225 | |
---|
| 226 | EstEID_log("successful"); |
---|
| 227 | |
---|
| 228 | return SUCCESS; |
---|
| 229 | } |
---|
| 230 | |
---|
| 231 | int EstEID_initializeCryptoki() { |
---|
| 232 | LOG_LOCATION; |
---|
| 233 | if (!EstEID_loadLibrary()) return FAILURE; |
---|
| 234 | #ifdef _WIN32 |
---|
| 235 | if (WAIT_OBJECT_0 != WaitForSingleObject(initialization_mutex, 0)) { |
---|
| 236 | EstEID_log("waiting for C_Initialize to complete"); |
---|
| 237 | WaitForSingleObject(initialization_mutex, INFINITE); |
---|
| 238 | } |
---|
| 239 | while (!initialization_completed) { |
---|
| 240 | ReleaseMutex(initialization_mutex); |
---|
| 241 | EstEID_log("waiting for C_Initialize to complete"); |
---|
| 242 | WaitForSingleObject(initialization_mutex, INFINITE); |
---|
| 243 | } |
---|
| 244 | ReleaseMutex(initialization_mutex); |
---|
| 245 | #else |
---|
| 246 | if (0 != pthread_mutex_trylock(&initialization_mutex)) { |
---|
| 247 | EstEID_log("waiting for C_Initialize to complete"); |
---|
| 248 | pthread_mutex_lock(&initialization_mutex); |
---|
| 249 | } |
---|
| 250 | while (!initialization_completed) { |
---|
| 251 | EstEID_log("waiting for C_Initialize to complete"); |
---|
| 252 | pthread_cond_wait(&initialization_condition, &initialization_mutex); |
---|
| 253 | } |
---|
| 254 | pthread_mutex_unlock(&initialization_mutex); |
---|
| 255 | #endif |
---|
| 256 | if (EstEID_CK_failure("C_Initialize", initialization_result)) return FAILURE; |
---|
| 257 | return SUCCESS; |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | int EstEID_loadSlotIDs(EstEID_Certs *certs) { |
---|
| 261 | CK_ULONG slotCount; |
---|
| 262 | EstEID_log("+++++++++++++++++++++++++++++++++++++ slotCount = %i", slotCount); |
---|
| 263 | |
---|
| 264 | FAIL_IF(EstEID_CK_failure("C_GetSlotList", fl->C_GetSlotList(CK_TRUE, NULL_PTR, &slotCount))); |
---|
| 265 | |
---|
| 266 | EstEID_log("+++++++++++++++++++++++++++++++++++++ slotCount = %i", slotCount); |
---|
| 267 | certs->count = slotCount; |
---|
| 268 | certs->slotIDs = (CK_SLOT_ID_PTR)malloc(sizeof(CK_SLOT_ID) * slotCount); |
---|
| 269 | certs->certs = (EstEID_Map *)malloc(sizeof(EstEID_Map) * slotCount); |
---|
| 270 | |
---|
| 271 | FAIL_IF(EstEID_CK_failure("C_GetSlotList", fl->C_GetSlotList(CK_TRUE, certs->slotIDs, &slotCount))); |
---|
| 272 | |
---|
| 273 | return SUCCESS; |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | //int EstEID_loadCertEntries(EstEID_Map cert, char *prefix, X509_NAME *x509Name) { |
---|
| 277 | int EstEID_loadCertEntries(EstEID_Map cert, char *prefix, struct X509_name_st *x509Name) { |
---|
| 278 | unsigned int i; |
---|
| 279 | // todo: error handling of all openssl functions |
---|
| 280 | unsigned int count = X509_NAME_entry_count(x509Name); |
---|
| 281 | for (i = 0; i < count; i++) { |
---|
| 282 | char *value; |
---|
| 283 | char name[1024]; |
---|
| 284 | X509_NAME_ENTRY *entry = X509_NAME_get_entry(x509Name, i); |
---|
| 285 | |
---|
| 286 | strcpy(name, prefix); |
---|
| 287 | OBJ_obj2txt(name + strlen(prefix), sizeof(name) - strlen(prefix), entry->object, 0); |
---|
| 288 | |
---|
| 289 | ASN1_STRING_to_UTF8((unsigned char **)&value, entry->value); |
---|
| 290 | |
---|
| 291 | EstEID_mapPutNoAlloc(cert, strdup(name), value); |
---|
| 292 | } |
---|
| 293 | return SUCCESS; |
---|
| 294 | } |
---|
| 295 | |
---|
| 296 | char *EstEID_getCertHash(char *certificate) { |
---|
| 297 | unsigned char certMD5[MD5_DIGEST_LENGTH]; |
---|
| 298 | char result[2 * MD5_DIGEST_LENGTH + 1] = ""; |
---|
| 299 | char chunk[3]; |
---|
| 300 | int i; |
---|
| 301 | |
---|
| 302 | MD5((unsigned char*)certificate, strlen(certificate), certMD5); |
---|
| 303 | |
---|
| 304 | for(i = 0; i < MD5_DIGEST_LENGTH; i++){ |
---|
| 305 | sprintf(chunk, "%02X", certMD5[i]); |
---|
| 306 | strcat(result, chunk); |
---|
| 307 | } |
---|
| 308 | |
---|
| 309 | EstEID_log("cert hash as HEX string: %s", result); |
---|
| 310 | return strdup(result); |
---|
| 311 | } |
---|
| 312 | |
---|
| 313 | char *EstEID_ASN1_TIME_toString(ASN1_TIME *time) { |
---|
| 314 | // ASN1_GENERALIZEDTIME.data format: yyyyMMddHHmmssZ |
---|
| 315 | ASN1_GENERALIZEDTIME *gt = ASN1_TIME_to_generalizedtime(time, NULL); |
---|
| 316 | char *data = (char *)gt->data; |
---|
| 317 | // result format dd.MM.yyyy HH:mm:ss |
---|
| 318 | // todo maybe: ASN1_GENERALIZEDTIME is in GMT -> should we adjust it? |
---|
| 319 | char *result = (char *)malloc(20); |
---|
| 320 | |
---|
| 321 | LOG_LOCATION; |
---|
| 322 | |
---|
| 323 | strncpy(result, data + 6, 2); |
---|
| 324 | strncpy(result + 3, data + 4, 2); |
---|
| 325 | strncpy(result + 6, data, 4); |
---|
| 326 | strncpy(result + 11, data + 8, 2); |
---|
| 327 | strncpy(result + 14, data + 10, 2); |
---|
| 328 | strncpy(result + 17, data + 12, 2); |
---|
| 329 | result[2] = result[5] = '.'; |
---|
| 330 | result[10] = ' '; |
---|
| 331 | result[13] = result[16] = ':'; |
---|
| 332 | result[19] = 0; |
---|
| 333 | ASN1_GENERALIZEDTIME_free(gt); |
---|
| 334 | return result; |
---|
| 335 | } |
---|
| 336 | |
---|
| 337 | int EstEID_loadCertInfoEntries(EstEID_Certs *certs, int index) { |
---|
| 338 | EstEID_Map cert = certs->certs[index]; |
---|
| 339 | CK_SLOT_ID slotID = certs->slotIDs[index]; |
---|
| 340 | CK_SESSION_HANDLE session; |
---|
| 341 | CK_OBJECT_CLASS objectClass = CKO_CERTIFICATE; |
---|
| 342 | CK_OBJECT_HANDLE objectHandle; |
---|
| 343 | CK_ULONG objectCount; |
---|
| 344 | CK_ATTRIBUTE searchAttribute ={CKA_CLASS, &objectClass, sizeof(objectClass)}; |
---|
| 345 | CK_ATTRIBUTE attribute = {CKA_VALUE, NULL_PTR, 0}; |
---|
| 346 | CK_ULONG certificateLength; |
---|
| 347 | CK_BYTE_PTR certificate; |
---|
| 348 | char *certMD5, *certSerialNumber, *b, *pem; |
---|
| 349 | const unsigned char *p; |
---|
| 350 | X509 *x509 ; |
---|
| 351 | unsigned long keyUsage; |
---|
| 352 | ASN1_BIT_STRING *usage; |
---|
| 353 | BIO *bio; |
---|
| 354 | int len; |
---|
| 355 | |
---|
| 356 | LOG_LOCATION; |
---|
| 357 | |
---|
| 358 | FAIL_IF(EstEID_CK_failure("C_OpenSession", fl->C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session))); |
---|
| 359 | |
---|
| 360 | if (EstEID_CK_failure("C_FindObjectsInit", fl->C_FindObjectsInit(session, &searchAttribute, 1))) return FAILURE; |
---|
| 361 | |
---|
| 362 | if (EstEID_CK_failure("C_FindObjects", fl->C_FindObjects(session, &objectHandle, 1, &objectCount))) return FAILURE; |
---|
| 363 | |
---|
| 364 | if (objectCount == 0) return SUCCESS; |
---|
| 365 | |
---|
| 366 | if (EstEID_CK_failure("C_GetAttributeValue", fl->C_GetAttributeValue(session, objectHandle, &attribute, 1))) return FAILURE; |
---|
| 367 | |
---|
| 368 | certificateLength = attribute.ulValueLen; |
---|
| 369 | certificate = (CK_BYTE_PTR)malloc(certificateLength); |
---|
| 370 | attribute.pValue = certificate; |
---|
| 371 | if (EstEID_CK_failure("C_GetAttributeValue", fl->C_GetAttributeValue(session, objectHandle, &attribute, 1))) return FAILURE; |
---|
| 372 | |
---|
| 373 | EstEID_log("cert = %p, certificate = %p, certificateLength = %i", cert, certificate, certificateLength); |
---|
| 374 | EstEID_mapPutNoAlloc(cert, strdup("certificateAsHex"), EstEID_bin2hex((char *)certificate, certificateLength)); |
---|
| 375 | |
---|
| 376 | p = certificate; |
---|
| 377 | x509 = d2i_X509(NULL, &p, certificateLength); |
---|
| 378 | |
---|
| 379 | certMD5 = EstEID_getCertHash((char*)certificate); |
---|
| 380 | FAIL_IF(EstEID_md5_failure(certMD5)); |
---|
| 381 | EstEID_mapPutNoAlloc(cert, strdup("certHash"), certMD5); |
---|
| 382 | free(certificate); |
---|
| 383 | |
---|
| 384 | // todo: error handling of all openssl functions |
---|
| 385 | |
---|
| 386 | EstEID_mapPutNoAlloc(cert, strdup("validTo"), EstEID_ASN1_TIME_toString(X509_get_notAfter(x509))); |
---|
| 387 | EstEID_mapPutNoAlloc(cert, strdup("validFrom"), EstEID_ASN1_TIME_toString(X509_get_notBefore(x509))); |
---|
| 388 | |
---|
| 389 | usage = (ASN1_BIT_STRING *)X509_get_ext_d2i(x509, NID_key_usage, NULL, NULL); |
---|
| 390 | if (usage->length > 0) keyUsage = usage->data[0]; |
---|
| 391 | ASN1_BIT_STRING_free(usage); |
---|
| 392 | |
---|
| 393 | if (keyUsage & X509v3_KU_DIGITAL_SIGNATURE) EstEID_mapPut(cert, "usageDigitalSignature", "TRUE"); |
---|
| 394 | if (keyUsage & X509v3_KU_NON_REPUDIATION) { |
---|
| 395 | EstEID_mapPut(cert, "usageNonRepudiation", "TRUE"); |
---|
| 396 | EstEID_mapPut(cert, "keyUsage", "Non-Repudiation"); // for compatibility with older plugin |
---|
| 397 | } |
---|
| 398 | |
---|
| 399 | EstEID_loadCertEntries(cert, "", X509_get_subject_name(x509)); |
---|
| 400 | |
---|
| 401 | certSerialNumber = (char*)malloc(33); |
---|
| 402 | snprintf(certSerialNumber, 32, "%lX", ASN1_INTEGER_get(X509_get_serialNumber(x509))); |
---|
| 403 | EstEID_mapPutNoAlloc(cert, strdup("certSerialNumber"), certSerialNumber); |
---|
| 404 | EstEID_loadCertEntries(cert, "issuer.", X509_get_issuer_name(x509)); |
---|
| 405 | |
---|
| 406 | bio = BIO_new(BIO_s_mem()); |
---|
| 407 | if (!PEM_write_bio_X509(bio, x509)) printf("Cannot create PEM\n"); |
---|
| 408 | len = BIO_get_mem_data(bio, &b); |
---|
| 409 | pem = (char *)malloc(len + 1); |
---|
| 410 | strncpy(pem, b, len); |
---|
| 411 | pem[len] = 0; |
---|
| 412 | BIO_free(bio); |
---|
| 413 | EstEID_mapPutNoAlloc(cert, strdup("certificateAsPEM"), pem); |
---|
| 414 | |
---|
| 415 | FAIL_IF(EstEID_CK_failure("C_CloseSession", fl->C_CloseSession(session))); |
---|
| 416 | return SUCCESS; |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | EstEID_Map EstEID_createCertMap(CK_TOKEN_INFO tokenInfo) { |
---|
| 420 | char *label = EstEID_createString(tokenInfo.label, sizeof(tokenInfo.label)); |
---|
| 421 | EstEID_Map cert = EstEID_mapPutNoAlloc(NULL, strdup("label"), label); |
---|
| 422 | |
---|
| 423 | char pinLen[8]; |
---|
| 424 | memset(pinLen, 0x0, 8); |
---|
| 425 | sprintf(pinLen, "%lu", tokenInfo.ulMinPinLen); |
---|
| 426 | EstEID_mapPut(cert, "minPinLen", pinLen); |
---|
| 427 | |
---|
| 428 | return cert; |
---|
| 429 | } |
---|
| 430 | |
---|
| 431 | int EstEID_loadCertInfo(EstEID_Certs *certs, int index) { |
---|
| 432 | CK_TOKEN_INFO tokenInfo; |
---|
| 433 | CK_SLOT_ID slotID = certs->slotIDs[index]; |
---|
| 434 | CK_SLOT_INFO slotInfo; |
---|
| 435 | |
---|
| 436 | LOG_LOCATION; |
---|
| 437 | EstEID_log("---------------------- index = %i", index); |
---|
| 438 | |
---|
| 439 | FAIL_IF(EstEID_CK_failure("C_GetSlotInfo", fl->C_GetSlotInfo(slotID, &slotInfo))); |
---|
| 440 | |
---|
| 441 | if (!(slotInfo.flags & CKF_TOKEN_PRESENT)) return SUCCESS; |
---|
| 442 | |
---|
| 443 | FAIL_IF(EstEID_CK_failure("C_GetTokenInfo", fl->C_GetTokenInfo(slotID, &tokenInfo))); |
---|
| 444 | |
---|
| 445 | certs->certs[index] = EstEID_createCertMap(tokenInfo); |
---|
| 446 | |
---|
| 447 | FAIL_UNLESS(EstEID_loadCertInfoEntries(certs, index)); |
---|
| 448 | |
---|
| 449 | return SUCCESS; |
---|
| 450 | } |
---|
| 451 | |
---|
| 452 | void EstEID_waitForSlotEvent() { |
---|
| 453 | CK_SLOT_ID slotID = 0; |
---|
| 454 | CK_RV result = fl->C_WaitForSlotEvent(0, &slotID, NULL_PTR); |
---|
| 455 | printf("result: %s, slotID: %lu\n", pkcs11_error_message(result), slotID); |
---|
| 456 | } |
---|
| 457 | |
---|
| 458 | int EstEID_tokensChanged() { |
---|
| 459 | CK_SLOT_ID slotID; |
---|
| 460 | int changed = FALSE; |
---|
| 461 | |
---|
| 462 | LOG_LOCATION; |
---|
| 463 | |
---|
| 464 | while (fl->C_WaitForSlotEvent(CKF_DONT_BLOCK, &slotID, NULL_PTR) == CKR_OK) { |
---|
| 465 | EstEID_log("C_WaitForSlotEvent() pass cycle 1"); |
---|
| 466 | changed = TRUE; |
---|
| 467 | } |
---|
| 468 | if (!changed) { |
---|
| 469 | while (fl->C_WaitForSlotEvent(CKF_DONT_BLOCK, &slotID, NULL_PTR) == CKR_OK) { |
---|
| 470 | EstEID_log("C_WaitForSlotEvent() pass cycle 2"); |
---|
| 471 | changed = TRUE; |
---|
| 472 | } |
---|
| 473 | } |
---|
| 474 | EstEID_log("tokens change %sdetected", changed ? "" : "not "); |
---|
| 475 | return changed; |
---|
| 476 | } |
---|
| 477 | |
---|
| 478 | EstEID_Certs *EstEID_loadCerts() { |
---|
| 479 | unsigned int i; |
---|
| 480 | LOG_LOCATION; |
---|
| 481 | if (!EstEID_initializeCryptoki()) { |
---|
| 482 | EstEID_log("cryptoki initialization result forces to return NULL"); |
---|
| 483 | return NULL; |
---|
| 484 | } |
---|
| 485 | if (certs && !EstEID_tokensChanged()){ |
---|
| 486 | EstEID_log("tokens not changed, returning existing certs"); |
---|
| 487 | return certs; |
---|
| 488 | } |
---|
| 489 | if (certs) EstEID_freeCerts(); |
---|
| 490 | |
---|
| 491 | certs = (EstEID_Certs *)malloc(sizeof(EstEID_Certs)); |
---|
| 492 | if (!EstEID_loadSlotIDs(certs)) return NULL; |
---|
| 493 | EstEID_log("Certs count=%lu", certs->count); |
---|
| 494 | for (i = 0; i < certs->count; i++) { |
---|
| 495 | if (!EstEID_loadCertInfo(certs, i)) return NULL; |
---|
| 496 | EstEID_log("slotID=%lu", certs->slotIDs[i]); |
---|
| 497 | EstEID_logMap(certs->certs[i]); |
---|
| 498 | } |
---|
| 499 | EstEID_log("returning %u fresh cert(s)", certs->count); |
---|
| 500 | return certs; |
---|
| 501 | } |
---|
| 502 | |
---|
| 503 | void EstEID_printCerts() { |
---|
| 504 | unsigned int i; |
---|
| 505 | if (!EstEID_loadCerts()) { |
---|
| 506 | printf("%s\n", EstEID_error); |
---|
| 507 | return; |
---|
| 508 | } |
---|
| 509 | for (i = 0; i < certs->count; i++) { |
---|
| 510 | EstEID_Map cert = certs->certs[i]; |
---|
| 511 | fprintf(stdout, "Slot: %lu\n", certs->slotIDs[i]); |
---|
| 512 | EstEID_mapPrint(stdout, cert); |
---|
| 513 | } |
---|
| 514 | } |
---|
| 515 | |
---|
| 516 | void EstEID_freeCerts() { |
---|
| 517 | unsigned int i; |
---|
| 518 | if (certs == NULL) return; |
---|
| 519 | for (i = 0; i < certs->count; i++) { |
---|
| 520 | EstEID_mapFree(certs->certs[i]); |
---|
| 521 | } |
---|
| 522 | if (certs->certs) free(certs->certs); |
---|
| 523 | if (certs->slotIDs) free(certs->slotIDs); |
---|
| 524 | free(certs); |
---|
| 525 | certs = NULL; |
---|
| 526 | } |
---|
| 527 | |
---|
| 528 | EstEID_Map EstEID_getNonRepudiationCert() { |
---|
| 529 | unsigned int i; |
---|
| 530 | if (!EstEID_loadCerts()) return NULL; |
---|
| 531 | for (i = 0; i < certs->count; i++) { |
---|
| 532 | EstEID_Map cert = certs->certs[i]; |
---|
| 533 | if (EstEID_mapGet(cert, "usageNonRepudiation")) return cert; |
---|
| 534 | } |
---|
| 535 | sprintf(EstEID_error, "non-repudiation certificate not found"); |
---|
| 536 | EstEID_errorCode = ESTEID_CERT_NOT_FOUND_ERROR; |
---|
| 537 | return NULL; |
---|
| 538 | } |
---|
| 539 | |
---|
| 540 | EstEID_Map EstEID_getNonRepudiationCertById(char* certID) { |
---|
| 541 | unsigned int i; |
---|
| 542 | if (!EstEID_loadCerts()) return NULL; |
---|
| 543 | for (i = 0; i < certs->count; i++) { |
---|
| 544 | EstEID_Map cert = certs->certs[i]; |
---|
| 545 | if(EstEID_mapGet(cert, "certHash") && !strcmp(certID, EstEID_mapGet(cert, "certHash"))) { |
---|
| 546 | return cert; |
---|
| 547 | } |
---|
| 548 | } |
---|
| 549 | sprintf(EstEID_error, "non-repudiation certificate not found"); |
---|
| 550 | EstEID_errorCode = ESTEID_CERT_NOT_FOUND_ERROR; |
---|
| 551 | return NULL; |
---|
| 552 | } |
---|
| 553 | |
---|
| 554 | const char *EstEID_getCertPropertyName(const char *name) { |
---|
| 555 | char *realName = (char *)name; |
---|
| 556 | if (!strcmp("CN", name)) realName = "commonName"; |
---|
| 557 | else if (!strcmp("id", name)) realName = "certHash"; |
---|
| 558 | else if (!strcmp("issuerCN", name)) realName = "issuer.commonName"; |
---|
| 559 | else if (!strcmp("cert", name)) realName = "certificateAsHex"; |
---|
| 560 | return realName; |
---|
| 561 | } |
---|
| 562 | |
---|
| 563 | char *EstEID_bin2hex(const char *bin, const int binLength) { |
---|
| 564 | int j; |
---|
| 565 | char *hex = (char *)malloc(binLength * 2 + 1); |
---|
| 566 | LOG_LOCATION; |
---|
| 567 | for (j = 0; j < binLength; j++) sprintf(hex + (j * 2), "%02X", (unsigned char)bin[j]); |
---|
| 568 | hex[binLength * 2] = '\0'; |
---|
| 569 | return hex; |
---|
| 570 | } |
---|
| 571 | |
---|