From ea6f7cfe1db8724b0952c35a51b5c02858707b89 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Wed, 10 Oct 2018 14:52:01 +0200 Subject: [PATCH] Added memory locking for secrets (#1491) When caching a PIN in memory or using an OpenSSL private key this data should not be swapped to disk. --- src/libopensc/ctx.c | 8 +++++ src/libopensc/libopensc.exports | 2 ++ src/libopensc/opensc.h | 2 ++ src/libopensc/pkcs15.c | 19 +++++++++-- src/libopensc/sc-ossl-compat.h | 13 +++++++ src/libopensc/sc.c | 60 +++++++++++++++++++++++++++++++++ src/pkcs11/misc.c | 8 ++--- 7 files changed, 105 insertions(+), 7 deletions(-) diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index f24a61ca..10a8e686 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -38,6 +38,7 @@ #include "common/libscdl.h" #include "internal.h" +#include "sc-ossl-compat.h" static int ignored_reader(sc_context_t *ctx, sc_reader_t *reader) { @@ -831,6 +832,13 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm) return r; } +#ifdef ENABLE_OPENSSL + if (!CRYPTO_secure_malloc_initialized()) { + /* XXX What's a reasonable amount of secure heap? */ + CRYPTO_secure_malloc_init(4096, 32); + } +#endif + process_config_file(ctx, &opts); sc_log(ctx, "==================================="); /* first thing in the log */ sc_log(ctx, "opensc version: %s", sc_get_version()); diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index 2d9b5e43..62628ea4 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -131,6 +131,8 @@ sc_lock sc_logout sc_make_cache_dir sc_mem_clear +sc_mem_secure_alloc +sc_mem_secure_free sc_mem_reverse sc_match_atr_block sc_path_print diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index a4e87d5b..0abbf6a9 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -1366,6 +1366,8 @@ int sc_base64_decode(const char *in, u8 *out, size_t outlen); * @param len length of the memory buffer */ void sc_mem_clear(void *ptr, size_t len); +void *sc_mem_secure_alloc(size_t len); +void sc_mem_secure_free(void *ptr, size_t len); int sc_mem_reverse(unsigned char *buf, size_t len); int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize); diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 12d6c834..14cf890f 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -2496,8 +2496,15 @@ sc_pkcs15_make_absolute_path(const struct sc_path *parent, struct sc_path *child void sc_pkcs15_free_object_content(struct sc_pkcs15_object *obj) { if (obj->content.value && obj->content.len) { - sc_mem_clear(obj->content.value, obj->content.len); - free(obj->content.value); + if (SC_PKCS15_TYPE_AUTH & obj->type + || SC_PKCS15_TYPE_SKEY & obj->type + || SC_PKCS15_TYPE_PRKEY & obj->type) { + /* clean everything that potentially contains a secret */ + sc_mem_clear(obj->content.value, obj->content.len); + sc_mem_secure_free(obj->content.value, obj->content.len); + } else { + free(obj->content.value); + } } obj->content.value = NULL; obj->content.len = 0; @@ -2521,7 +2528,13 @@ sc_pkcs15_allocate_object_content(struct sc_context *ctx, struct sc_pkcs15_objec /* Need to pass by temporary variable, * because 'value' and 'content.value' pointers can be the sames. */ - tmp_buf = calloc(sizeof *tmp_buf, len); + if (SC_PKCS15_TYPE_AUTH & obj->type + || SC_PKCS15_TYPE_SKEY & obj->type + || SC_PKCS15_TYPE_PRKEY & obj->type) { + tmp_buf = sc_mem_secure_alloc(len); + } else { + tmp_buf = malloc(len); + } if (!tmp_buf) return SC_ERROR_OUT_OF_MEMORY; diff --git a/src/libopensc/sc-ossl-compat.h b/src/libopensc/sc-ossl-compat.h index 9185dd07..192727f6 100644 --- a/src/libopensc/sc-ossl-compat.h +++ b/src/libopensc/sc-ossl-compat.h @@ -230,6 +230,19 @@ static sc_ossl_inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) } #endif /* OPENSSL_NO_EC */ +static sc_ossl_inline int CRYPTO_secure_malloc_init(size_t size, int minsize) +{ + return 0; +} + +static sc_ossl_inline int CRYPTO_secure_malloc_initialized() +{ + return 0; +} + +#else + +#include #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index ea634100..2052998b 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -34,6 +34,7 @@ #include /* for OPENSSL_cleanse */ #endif + #include "internal.h" #ifdef PACKAGE_VERSION @@ -42,6 +43,19 @@ static const char *sc_version = PACKAGE_VERSION; static const char *sc_version = "(undef)"; #endif +#ifdef _WIN32 +#include +#define PAGESIZE 0 +#else +#include +#include +#include +#ifndef PAGESIZE +#define PAGESIZE 0 +#endif +#endif +static size_t page_size = PAGESIZE; + const char *sc_get_version(void) { return sc_version; @@ -826,6 +840,52 @@ int _sc_parse_atr(sc_reader_t *reader) return SC_SUCCESS; } +static void init_page_size() +{ + if (page_size == 0) { +#ifdef _WIN32 + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + page_size = system_info.dwPageSize; +#else + page_size = sysconf(_SC_PAGESIZE); + if ((long) page_size < 0) { + page_size = 0; + } +#endif + } +} + +void *sc_mem_secure_alloc(size_t len) +{ + void *p; + + init_page_size(); + if (page_size > 0) { + size_t pages = (len + page_size - 1) / page_size; + len = pages * page_size; + } + + p = malloc(len); +#ifdef _WIN32 + VirtualLock(p, len); +#else + mlock(p, len); +#endif + + return p; +} + +void sc_mem_secure_free(void *ptr, size_t len) +{ +#ifdef _WIN32 + VirtualUnlock(ptr, len); +#else + munlock(ptr, len); +#endif + free(ptr); +} + void sc_mem_clear(void *ptr, size_t len) { if (len > 0) { diff --git a/src/pkcs11/misc.c b/src/pkcs11/misc.c index ab77c0cb..e4798319 100644 --- a/src/pkcs11/misc.c +++ b/src/pkcs11/misc.c @@ -187,7 +187,7 @@ CK_RV push_login_state(struct sc_pkcs11_slot *slot, } if (pPin && ulPinLen) { - login->pPin = calloc((sizeof *pPin), ulPinLen); + login->pPin = sc_mem_secure_alloc((sizeof *pPin)*ulPinLen); if (login->pPin == NULL) { goto err; } @@ -207,7 +207,7 @@ err: if (login) { if (login->pPin) { sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_free(login->pPin, login->ulPinLen); } free(login); } @@ -223,7 +223,7 @@ void pop_login_state(struct sc_pkcs11_slot *slot) struct sc_pkcs11_login *login = list_get_at(&slot->logins, size-1); if (login) { sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_free(login->pPin, login->ulPinLen); free(login); } if (0 > list_delete_at(&slot->logins, size-1)) @@ -238,7 +238,7 @@ void pop_all_login_states(struct sc_pkcs11_slot *slot) struct sc_pkcs11_login *login = list_fetch(&slot->logins); while (login) { sc_mem_clear(login->pPin, login->ulPinLen); - free(login->pPin); + sc_mem_secure_free(login->pPin, login->ulPinLen); free(login); login = list_fetch(&slot->logins); }