From 74924dcb3802640d7e2ae2e80ca6515d0a53de7a Mon Sep 17 00:00:00 2001 From: Rich Salz Date: Fri, 24 Apr 2015 16:39:40 -0400 Subject: [PATCH] More secure storage of key material. Add secure heap for storage of private keys (when possible). Add BIO_s_secmem(), CBIGNUM, etc. Add BIO_CTX_secure_new so all BIGNUM's in the context are secure. Contributed by Akamai Technologies under the Corporate CLA. Reviewed-by: Richard Levitte --- CHANGES | 5 + crypto/Makefile | 8 +- crypto/asn1/x_bignum.c | 32 +- crypto/bio/bss_mem.c | 36 +- crypto/bn/bn_ctx.c | 79 +++-- crypto/bn/bn_lib.c | 37 +- crypto/buffer/buffer.c | 42 ++- crypto/dh/dh_ameth.c | 3 +- crypto/dh/dh_key.c | 2 +- crypto/dsa/dsa_ameth.c | 3 +- crypto/dsa/dsa_asn1.c | 2 +- crypto/dsa/dsa_key.c | 2 +- crypto/ec/ec_asn1.c | 2 + crypto/mem.c | 73 +++- crypto/rsa/rsa_asn1.c | 12 +- crypto/rsa/rsa_gen.c | 12 +- crypto/sec_mem.c | 513 ++++++++++++++++++++++++++++ doc/crypto/BIO_s_mem.pod | 7 + doc/crypto/BN_CTX_new.pod | 15 +- doc/crypto/CRYPTO_secure_malloc.pod | 91 +++++ doc/crypto/bio.pod | 1 + doc/crypto/bn.pod | 1 + doc/crypto/buffer.pod | 14 +- include/openssl/bio.h | 1 + include/openssl/bn.h | 3 + include/openssl/buffer.h | 4 + include/openssl/crypto.h | 23 +- test/Makefile | 14 +- test/secmemtest.c | 34 ++ util/libeay.num | 14 + 30 files changed, 1009 insertions(+), 76 deletions(-) create mode 100644 crypto/sec_mem.c create mode 100644 doc/crypto/CRYPTO_secure_malloc.pod create mode 100644 test/secmemtest.c diff --git a/CHANGES b/CHANGES index fae11230fc..88b3d67273 100644 --- a/CHANGES +++ b/CHANGES @@ -126,6 +126,11 @@ NULL. Remove the non-null checks from callers. Save much code. [Rich Salz] + *) Add secure heap for storage of private keys (when possible). + Add BIO_s_secmem(), CBIGNUM, etc. + Contributed by Akamai Technologies under our Corporate CLA. + [Rich Salz] + *) Experimental support for a new, fast, unbiased prime candidate generator, bn_probable_prime_dh_coprime(). Not currently used by any prime generator. [Felix Laurie von Massenbach ] diff --git a/crypto/Makefile b/crypto/Makefile index 99cd65c5bc..b2cf528a91 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -36,10 +36,10 @@ LIB= $(TOP)/libcrypto.a SHARED_LIB= libcrypto$(SHLIB_EXT) LIBSRC= cryptlib.c mem.c mem_clr.c mem_dbg.c cversion.c ex_data.c cpt_err.c \ ebcdic.c uid.c o_time.c o_str.c o_dir.c thr_id.c lock.c fips_ers.c \ - o_init.c o_fips.c + o_init.c o_fips.c sec_mem.c LIBOBJ= cryptlib.o mem.o mem_dbg.o cversion.o ex_data.o cpt_err.o \ ebcdic.o uid.o o_time.o o_str.o o_dir.o thr_id.o lock.o fips_ers.o \ - o_init.o o_fips.o $(CPUID_OBJ) + o_init.o o_fips.o sec_mem.o $(CPUID_OBJ) SRC= $(LIBSRC) @@ -208,6 +208,10 @@ o_time.o: ../include/openssl/crypto.h ../include/openssl/e_os2.h o_time.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h o_time.o: ../include/openssl/ossl_typ.h ../include/openssl/safestack.h o_time.o: ../include/openssl/stack.h ../include/openssl/symhacks.h o_time.c +sec_mem.o: ../e_os.h ../include/openssl/crypto.h ../include/openssl/e_os2.h +sec_mem.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h +sec_mem.o: ../include/openssl/ossl_typ.h ../include/openssl/safestack.h +sec_mem.o: ../include/openssl/stack.h ../include/openssl/symhacks.h sec_mem.c thr_id.o: ../e_os.h ../include/openssl/bio.h ../include/openssl/buffer.h thr_id.o: ../include/openssl/crypto.h ../include/openssl/e_os2.h thr_id.o: ../include/openssl/err.h ../include/openssl/lhash.h diff --git a/crypto/asn1/x_bignum.c b/crypto/asn1/x_bignum.c index 8307a2d729..66ce000827 100644 --- a/crypto/asn1/x_bignum.c +++ b/crypto/asn1/x_bignum.c @@ -72,12 +72,15 @@ #define BN_SENSITIVE 1 static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it); +static int bn_secure_new(ASN1_VALUE **pval, const ASN1_ITEM *it); static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it); static int bn_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it); static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it); +static int bn_secure_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, + int utype, char *free_cont, const ASN1_ITEM *it); static ASN1_PRIMITIVE_FUNCS bignum_pf = { NULL, 0, @@ -88,12 +91,21 @@ static ASN1_PRIMITIVE_FUNCS bignum_pf = { bn_i2c }; +static ASN1_PRIMITIVE_FUNCS cbignum_pf = { + NULL, 0, + bn_secure_new, + bn_free, + 0, + bn_secure_c2i, + bn_i2c +}; + ASN1_ITEM_start(BIGNUM) ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &bignum_pf, 0, "BIGNUM" ASN1_ITEM_end(BIGNUM) ASN1_ITEM_start(CBIGNUM) - ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &bignum_pf, BN_SENSITIVE, "BIGNUM" + ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &cbignum_pf, BN_SENSITIVE, "CBIGNUM" ASN1_ITEM_end(CBIGNUM) static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it) @@ -105,6 +117,15 @@ static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it) return 0; } +static int bn_secure_new(ASN1_VALUE **pval, const ASN1_ITEM *it) +{ + *pval = (ASN1_VALUE *)BN_secure_new(); + if (*pval) + return 1; + else + return 0; +} + static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it) { if (!*pval) @@ -141,6 +162,7 @@ static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it) { BIGNUM *bn; + if (!*pval) bn_new(pval, it); bn = (BIGNUM *)*pval; @@ -150,3 +172,11 @@ static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, } return 1; } + +static int bn_secure_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, + int utype, char *free_cont, const ASN1_ITEM *it) +{ + if (!*pval) + bn_secure_new(pval, it); + return bn_c2i(pval, cont, len, utype, free_cont, it); +} diff --git a/crypto/bio/bss_mem.c b/crypto/bio/bss_mem.c index a1f5e8d960..485a8bf663 100644 --- a/crypto/bio/bss_mem.c +++ b/crypto/bio/bss_mem.c @@ -67,6 +67,7 @@ static int mem_puts(BIO *h, const char *str); static int mem_gets(BIO *h, char *str, int size); static long mem_ctrl(BIO *h, int cmd, long arg1, void *arg2); static int mem_new(BIO *h); +static int secmem_new(BIO *h); static int mem_free(BIO *data); static BIO_METHOD mem_method = { BIO_TYPE_MEM, @@ -80,6 +81,18 @@ static BIO_METHOD mem_method = { mem_free, NULL, }; +static BIO_METHOD secmem_method = { + BIO_TYPE_MEM, + "secure memory buffer", + mem_write, + mem_read, + mem_puts, + mem_gets, + mem_ctrl, + secmem_new, + mem_free, + NULL, +}; /* * bio->num is used to hold the value to return on 'empty', if it is 0, @@ -91,6 +104,11 @@ BIO_METHOD *BIO_s_mem(void) return (&mem_method); } +BIO_METHOD *BIO_s_secmem(void) +{ + return(&secmem_method); +} + BIO *BIO_new_mem_buf(void *buf, int len) { BIO *ret; @@ -114,17 +132,27 @@ BIO *BIO_new_mem_buf(void *buf, int len) return ret; } -static int mem_new(BIO *bi) +static int mem_init(BIO *bi, unsigned long flags) { BUF_MEM *b; - if ((b = BUF_MEM_new()) == NULL) - return (0); + if ((b = BUF_MEM_new_ex(flags)) == NULL) + return(0); bi->shutdown = 1; bi->init = 1; bi->num = -1; bi->ptr = (char *)b; - return (1); + return(1); +} + +static int mem_new(BIO *bi) +{ + return (mem_init(bi, 0L)); +} + +static int secmem_new(BIO *bi) +{ + return (mem_init(bi, BUF_MEM_FLAG_SECURE)); } static int mem_free(BIO *a) diff --git a/crypto/bn/bn_ctx.c b/crypto/bn/bn_ctx.c index c023303b67..660e626a91 100644 --- a/crypto/bn/bn_ctx.c +++ b/crypto/bn/bn_ctx.c @@ -103,7 +103,7 @@ typedef struct bignum_pool { } BN_POOL; static void BN_POOL_init(BN_POOL *); static void BN_POOL_finish(BN_POOL *); -static BIGNUM *BN_POOL_get(BN_POOL *); +static BIGNUM *BN_POOL_get(BN_POOL *, int); static void BN_POOL_release(BN_POOL *, unsigned int); /************/ @@ -138,6 +138,8 @@ struct bignum_ctx { int err_stack; /* Block "gets" until an "end" (compatibility behaviour) */ int too_many; + /* Flags. */ + int flags; }; /* Enable this to find BN_CTX bugs */ @@ -186,8 +188,9 @@ static void ctxdbg(BN_CTX *ctx) BN_CTX *BN_CTX_new(void) { - BN_CTX *ret = OPENSSL_malloc(sizeof(*ret)); - if (!ret) { + BN_CTX *ret; + + if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL) { BNerr(BN_F_BN_CTX_NEW, ERR_R_MALLOC_FAILURE); return NULL; } @@ -197,6 +200,16 @@ BN_CTX *BN_CTX_new(void) ret->used = 0; ret->err_stack = 0; ret->too_many = 0; + ret->flags = 0; + return ret; +} + +BN_CTX *BN_CTX_secure_new(void) +{ + BN_CTX *ret = BN_CTX_new(); + + if (ret) + ret->flags = BN_FLG_SECURE; return ret; } @@ -258,10 +271,11 @@ void BN_CTX_end(BN_CTX *ctx) BIGNUM *BN_CTX_get(BN_CTX *ctx) { BIGNUM *ret; + CTXDBG_ENTRY("BN_CTX_get", ctx); if (ctx->err_stack || ctx->too_many) return NULL; - if ((ret = BN_POOL_get(&ctx->pool)) == NULL) { + if ((ret = BN_POOL_get(&ctx->pool, ctx->flags)) == NULL) { /* * Setting too_many prevents repeated "get" attempts from cluttering * the error stack. @@ -289,26 +303,23 @@ static void BN_STACK_init(BN_STACK *st) static void BN_STACK_finish(BN_STACK *st) { - if (st->size) - OPENSSL_free(st->indexes); + OPENSSL_free(st->indexes); + st->indexes = NULL; } static int BN_STACK_push(BN_STACK *st, unsigned int idx) { - if (st->depth == st->size) + if (st->depth == st->size) { /* Need to expand */ - { - unsigned int newsize = (st->size ? - (st->size * 3 / 2) : BN_CTX_START_FRAMES); - unsigned int *newitems = OPENSSL_malloc(newsize * - sizeof(unsigned int)); - if (!newitems) + unsigned int newsize = + st->size ? (st->size * 3 / 2) : BN_CTX_START_FRAMES; + unsigned int *newitems = OPENSSL_malloc(sizeof(*newitems) * newsize); + if (newitems == NULL) return 0; if (st->depth) - memcpy(newitems, st->indexes, st->depth * sizeof(unsigned int)); - if (st->size) - OPENSSL_free(st->indexes); + memcpy(newitems, st->indexes, sizeof(*newitems) * st->depth); + OPENSSL_free(st->indexes); st->indexes = newitems; st->size = newsize; } @@ -333,14 +344,13 @@ static void BN_POOL_init(BN_POOL *p) static void BN_POOL_finish(BN_POOL *p) { + unsigned int loop; + BIGNUM *bn; + while (p->head) { - unsigned int loop = 0; - BIGNUM *bn = p->head->vals; - while (loop++ < BN_CTX_POOL_SIZE) { + for (loop = 0, bn = p->head->vals; loop++ < BN_CTX_POOL_SIZE; bn++) if (bn->d) BN_clear_free(bn); - bn++; - } p->current = p->head->next; OPENSSL_free(p->head); p->head = p->current; @@ -348,22 +358,25 @@ static void BN_POOL_finish(BN_POOL *p) } -static BIGNUM *BN_POOL_get(BN_POOL *p) +static BIGNUM *BN_POOL_get(BN_POOL *p, int flag) { + BIGNUM *bn; + unsigned int loop; + + /* Full; allocate a new pool item and link it in. */ if (p->used == p->size) { - BIGNUM *bn; - unsigned int loop = 0; BN_POOL_ITEM *item = OPENSSL_malloc(sizeof(*item)); - if (!item) + if (item == NULL) return NULL; - /* Initialise the structure */ - bn = item->vals; - while (loop++ < BN_CTX_POOL_SIZE) - BN_init(bn++); + for (loop = 0, bn = item->vals; loop++ < BN_CTX_POOL_SIZE; bn++) { + BN_init(bn); + if ((flag & BN_FLG_SECURE) != 0) + BN_set_flags(bn, BN_FLG_SECURE); + } item->prev = p->tail; item->next = NULL; - /* Link it in */ - if (!p->head) + + if (p->head == NULL) p->head = p->current = p->tail = item; else { p->tail->next = item; @@ -375,6 +388,7 @@ static BIGNUM *BN_POOL_get(BN_POOL *p) /* Return the first bignum from the new pool */ return item->vals; } + if (!p->used) p->current = p->head; else if ((p->used % BN_CTX_POOL_SIZE) == 0) @@ -385,10 +399,11 @@ static BIGNUM *BN_POOL_get(BN_POOL *p) static void BN_POOL_release(BN_POOL *p, unsigned int num) { unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE; + p->used -= num; while (num--) { bn_check_top(p->current->vals + offset); - if (!offset) { + if (offset == 0) { offset = BN_CTX_POOL_SIZE - 1; p->current = p->current->prev; } else diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c index 4dabe26b10..b5f827a36c 100644 --- a/crypto/bn/bn_lib.c +++ b/crypto/bn/bn_lib.c @@ -232,8 +232,12 @@ void BN_clear_free(BIGNUM *a) bn_check_top(a); if (a->d != NULL) { OPENSSL_cleanse(a->d, a->dmax * sizeof(a->d[0])); - if (!(BN_get_flags(a, BN_FLG_STATIC_DATA))) - OPENSSL_free(a->d); + if (!(BN_get_flags(a, BN_FLG_STATIC_DATA))) { + if (BN_get_flags(a,BN_FLG_SECURE)) + OPENSSL_secure_free(a->d); + else + OPENSSL_free(a->d); + } } i = BN_get_flags(a, BN_FLG_MALLOCED); OPENSSL_cleanse(a, sizeof(BIGNUM)); @@ -247,7 +251,12 @@ void BN_free(BIGNUM *a) return; bn_check_top(a); if (!BN_get_flags(a, BN_FLG_STATIC_DATA)) - OPENSSL_free(a->d); + if ((a->d != NULL) && !(BN_get_flags(a, BN_FLG_STATIC_DATA))) { + if (BN_get_flags(a, BN_FLG_SECURE)) + OPENSSL_secure_free(a->d); + else + OPENSSL_free(a->d); + } if (a->flags & BN_FLG_MALLOCED) OPENSSL_free(a); else { @@ -281,6 +290,14 @@ BIGNUM *BN_new(void) return (ret); } + BIGNUM *BN_secure_new(void) + { + BIGNUM *ret = BN_new(); + if (ret) + ret->flags |= BN_FLG_SECURE; + return (ret); + } + /* This is used both by bn_expand2() and bn_dup_expand() */ /* The caller MUST check that words > b->dmax before calling this */ static BN_ULONG *bn_expand_internal(const BIGNUM *b, int words) @@ -299,7 +316,10 @@ static BN_ULONG *bn_expand_internal(const BIGNUM *b, int words) BNerr(BN_F_BN_EXPAND_INTERNAL, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA); return (NULL); } - a = A = OPENSSL_malloc(sizeof(*a) * words); + if (BN_get_flags(b,BN_FLG_SECURE)) + a = A = OPENSSL_secure_malloc(words * sizeof(*a)); + else + a = A = OPENSSL_malloc(words * sizeof(*a)); if (A == NULL) { BNerr(BN_F_BN_EXPAND_INTERNAL, ERR_R_MALLOC_FAILURE); return (NULL); @@ -378,7 +398,12 @@ BIGNUM *bn_expand2(BIGNUM *b, int words) BN_ULONG *a = bn_expand_internal(b, words); if (!a) return NULL; - OPENSSL_free(b->d); + if (b->d) { + if (BN_get_flags(b,BN_FLG_SECURE)) + OPENSSL_secure_free(b->d); + else + OPENSSL_free(b->d); + } b->d = a; b->dmax = words; } @@ -395,7 +420,7 @@ BIGNUM *BN_dup(const BIGNUM *a) return NULL; bn_check_top(a); - t = BN_new(); + t = BN_get_flags(a, BN_FLG_SECURE) ? BN_secure_new() : BN_new(); if (t == NULL) return NULL; if (!BN_copy(t, a)) { diff --git a/crypto/buffer/buffer.c b/crypto/buffer/buffer.c index 2beacce6d7..5ee11f4c70 100644 --- a/crypto/buffer/buffer.c +++ b/crypto/buffer/buffer.c @@ -67,6 +67,16 @@ */ #define LIMIT_BEFORE_EXPANSION 0x5ffffffc +BUF_MEM *BUF_MEM_new_ex(unsigned long flags) +{ + BUF_MEM *ret; + + ret = BUF_MEM_new(); + if (ret != NULL) + ret->flags = flags; + return (ret); +} + BUF_MEM *BUF_MEM_new(void) { BUF_MEM *ret; @@ -76,6 +86,7 @@ BUF_MEM *BUF_MEM_new(void) BUFerr(BUF_F_BUF_MEM_NEW, ERR_R_MALLOC_FAILURE); return (NULL); } + ret->flags = 0; ret->length = 0; ret->max = 0; ret->data = NULL; @@ -88,11 +99,30 @@ void BUF_MEM_free(BUF_MEM *a) return; if (a->data != NULL) { - OPENSSL_clear_free(a->data, a->max); + memset(a->data, 0, (unsigned int)a->max); + if (a->flags & BUF_MEM_FLAG_SECURE) + OPENSSL_secure_free(a->data); + else + OPENSSL_clear_free(a->data, a->max); } OPENSSL_free(a); } +/* Allocate a block of secure memory; copy over old data if there + * was any, and then free it. */ +static char *sec_alloc_realloc(BUF_MEM *str, size_t len) +{ + char *ret; + + ret = OPENSSL_secure_malloc(len); + if (str->data != NULL) { + if (ret != NULL) + memcpy(ret, str->data, str->length); + OPENSSL_secure_free(str->data); + } + return (ret); +} + size_t BUF_MEM_grow(BUF_MEM *str, size_t len) { char *ret; @@ -113,7 +143,10 @@ size_t BUF_MEM_grow(BUF_MEM *str, size_t len) return 0; } n = (len + 3) / 3 * 4; - ret = OPENSSL_realloc(str->data, n); + if ((str->flags & BUF_MEM_FLAG_SECURE)) + ret = sec_alloc_realloc(str, n); + else + ret = OPENSSL_realloc(str->data, n); if (ret == NULL) { BUFerr(BUF_F_BUF_MEM_GROW, ERR_R_MALLOC_FAILURE); len = 0; @@ -147,7 +180,10 @@ size_t BUF_MEM_grow_clean(BUF_MEM *str, size_t len) return 0; } n = (len + 3) / 3 * 4; - ret = OPENSSL_realloc_clean(str->data, str->max, n); + if ((str->flags & BUF_MEM_FLAG_SECURE)) + ret = sec_alloc_realloc(str, n); + else + ret = OPENSSL_realloc_clean(str->data, str->max, n); if (ret == NULL) { BUFerr(BUF_F_BUF_MEM_GROW_CLEAN, ERR_R_MALLOC_FAILURE); len = 0; diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c index 98f8570a2f..efb3d805e8 100644 --- a/crypto/dh/dh_ameth.c +++ b/crypto/dh/dh_ameth.c @@ -228,7 +228,8 @@ static int dh_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) goto decerr; /* We have parameters now set private key */ - if ((dh->priv_key = ASN1_INTEGER_to_BN(privkey, NULL)) == NULL) { + if ((dh->priv_key = BN_secure_new()) == NULL + || !ASN1_INTEGER_to_BN(privkey, dh->priv_key)) { DHerr(DH_F_DH_PRIV_DECODE, DH_R_BN_ERROR); goto dherr; } diff --git a/crypto/dh/dh_key.c b/crypto/dh/dh_key.c index ff91d41d79..b6c3038976 100644 --- a/crypto/dh/dh_key.c +++ b/crypto/dh/dh_key.c @@ -125,7 +125,7 @@ static int generate_key(DH *dh) goto err; if (dh->priv_key == NULL) { - priv_key = BN_new(); + priv_key = BN_secure_new(); if (priv_key == NULL) goto err; generate_new_key = 1; diff --git a/crypto/dsa/dsa_ameth.c b/crypto/dsa/dsa_ameth.c index 01b3497552..73dd158cee 100644 --- a/crypto/dsa/dsa_ameth.c +++ b/crypto/dsa/dsa_ameth.c @@ -245,7 +245,8 @@ static int dsa_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) if ((dsa = d2i_DSAparams(NULL, &pm, pmlen)) == NULL) goto decerr; /* We have parameters now set private key */ - if ((dsa->priv_key = ASN1_INTEGER_to_BN(privkey, NULL)) == NULL) { + if ((dsa->priv_key = BN_secure_new()) == NULL + || !ASN1_INTEGER_to_BN(privkey, dsa->priv_key)) { DSAerr(DSA_F_DSA_PRIV_DECODE, DSA_R_BN_ERROR); goto dsaerr; } diff --git a/crypto/dsa/dsa_asn1.c b/crypto/dsa/dsa_asn1.c index bb2434e20e..85db147822 100644 --- a/crypto/dsa/dsa_asn1.c +++ b/crypto/dsa/dsa_asn1.c @@ -113,7 +113,7 @@ ASN1_SEQUENCE_cb(DSAPrivateKey, dsa_cb) = { ASN1_SIMPLE(DSA, q, BIGNUM), ASN1_SIMPLE(DSA, g, BIGNUM), ASN1_SIMPLE(DSA, pub_key, BIGNUM), - ASN1_SIMPLE(DSA, priv_key, BIGNUM) + ASN1_SIMPLE(DSA, priv_key, CBIGNUM) } ASN1_SEQUENCE_END_cb(DSA, DSAPrivateKey) IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAPrivateKey, DSAPrivateKey) diff --git a/crypto/dsa/dsa_key.c b/crypto/dsa/dsa_key.c index 01a83e0018..19d21eacf7 100644 --- a/crypto/dsa/dsa_key.c +++ b/crypto/dsa/dsa_key.c @@ -82,7 +82,7 @@ static int dsa_builtin_keygen(DSA *dsa) goto err; if (dsa->priv_key == NULL) { - if ((priv_key = BN_new()) == NULL) + if ((priv_key = BN_secure_new()) == NULL) goto err; } else priv_key = dsa->priv_key; diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c index ebafc10de7..3f971aa46c 100644 --- a/crypto/ec/ec_asn1.c +++ b/crypto/ec/ec_asn1.c @@ -1023,6 +1023,8 @@ EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len) ret->version = priv_key->version; if (priv_key->privateKey) { + if (ret->priv_key == NULL) + ret->priv_key = BN_secure_new(); ret->priv_key = BN_bin2bn(ASN1_STRING_data(priv_key->privateKey), ASN1_STRING_length(priv_key->privateKey), ret->priv_key); diff --git a/crypto/mem.c b/crypto/mem.c index b98e44fc5a..56c3585865 100644 --- a/crypto/mem.c +++ b/crypto/mem.c @@ -94,6 +94,15 @@ static void *(*realloc_ex_func) (void *, size_t, const char *file, int line) static void (*free_func) (void *) = free; +static void *(*malloc_secure_func)(size_t) = malloc; +static void *default_malloc_secure_ex(size_t num, const char *file, int line) +{ + return malloc_secure_func(num); +} +static void *(*malloc_secure_ex_func)(size_t, const char *file, int line) + = default_malloc_secure_ex; +static void (*free_secure_func)(void *) = free; + static void *(*malloc_locked_func) (size_t) = malloc; static void *default_malloc_locked_ex(size_t num, const char *file, int line) { @@ -145,6 +154,11 @@ int CRYPTO_set_mem_functions(void *(*m) (size_t), void *(*r) (void *, size_t), realloc_func = r; realloc_ex_func = default_realloc_ex; free_func = f; + /* If user wants to intercept the secure or locked functions, do it + * after the basic functions. */ + malloc_secure_func = m; + malloc_secure_ex_func = default_malloc_secure_ex; + free_secure_func = f; malloc_locked_func = m; malloc_locked_ex_func = default_malloc_locked_ex; free_locked_func = f; @@ -164,6 +178,44 @@ int CRYPTO_set_mem_ex_functions(void *(*m) (size_t, const char *, int), realloc_func = 0; realloc_ex_func = r; free_func = f; + malloc_secure_func = 0; + malloc_secure_ex_func = m; + free_secure_func = f; + malloc_locked_func = 0; + malloc_locked_ex_func = m; + free_locked_func = f; + return 1; +} + +int CRYPTO_set_secure_mem_functions(void *(*m)(size_t), void (*f)(void *)) +{ + /* Dummy call just to ensure OPENSSL_init() gets linked in */ + OPENSSL_init(); + if (!allow_customize) + return 0; + if ((m == 0) || (f == 0)) + return 0; + malloc_secure_func = m; + malloc_secure_ex_func = default_malloc_secure_ex; + free_secure_func = f; + /* If user wants to intercept the locked functions, do it after + * the secure functions. */ + malloc_locked_func = m; + malloc_locked_ex_func = default_malloc_secure_ex; + free_locked_func = f; + return 1; +} + +int CRYPTO_set_secure_mem_ex_functions(void *(*m)(size_t, const char *, int), + void (*f)(void *)) +{ + if (!allow_customize) + return 0; + if ((m == NULL) || (f == NULL)) + return 0; + malloc_secure_func = 0; + malloc_secure_ex_func = m; + free_secure_func = f; malloc_locked_func = 0; malloc_locked_ex_func = m; free_locked_func = f; @@ -191,7 +243,7 @@ int CRYPTO_set_locked_mem_ex_functions(void *(*m) (size_t, const char *, int), return 0; malloc_locked_func = 0; malloc_locked_ex_func = m; - free_func = f; + free_locked_func = f; return 1; } @@ -236,6 +288,25 @@ void CRYPTO_get_mem_ex_functions(void *(**m) (size_t, const char *, int), *f = free_func; } +void CRYPTO_get_secure_mem_functions(void *(**m)(size_t), void (**f)(void *)) +{ + if (m != NULL) + *m = (malloc_secure_ex_func == default_malloc_secure_ex) ? + malloc_secure_func : 0; + if (f != NULL) + *f=free_secure_func; + } + +void CRYPTO_get_secure_mem_ex_functions(void *(**m)(size_t,const char *,int), + void (**f)(void *)) +{ + if (m != NULL) + *m = (malloc_secure_ex_func != default_malloc_secure_ex) ? + malloc_secure_ex_func : 0; + if (f != NULL) + *f=free_secure_func; +} + void CRYPTO_get_locked_mem_functions(void *(**m) (size_t), void (**f) (void *)) { diff --git a/crypto/rsa/rsa_asn1.c b/crypto/rsa/rsa_asn1.c index 0cf1b2ab94..8061aed5a9 100644 --- a/crypto/rsa/rsa_asn1.c +++ b/crypto/rsa/rsa_asn1.c @@ -85,12 +85,12 @@ ASN1_SEQUENCE_cb(RSAPrivateKey, rsa_cb) = { ASN1_SIMPLE(RSA, version, LONG), ASN1_SIMPLE(RSA, n, BIGNUM), ASN1_SIMPLE(RSA, e, BIGNUM), - ASN1_SIMPLE(RSA, d, BIGNUM), - ASN1_SIMPLE(RSA, p, BIGNUM), - ASN1_SIMPLE(RSA, q, BIGNUM), - ASN1_SIMPLE(RSA, dmp1, BIGNUM), - ASN1_SIMPLE(RSA, dmq1, BIGNUM), - ASN1_SIMPLE(RSA, iqmp, BIGNUM) + ASN1_SIMPLE(RSA, d, CBIGNUM), + ASN1_SIMPLE(RSA, p, CBIGNUM), + ASN1_SIMPLE(RSA, q, CBIGNUM), + ASN1_SIMPLE(RSA, dmp1, CBIGNUM), + ASN1_SIMPLE(RSA, dmq1, CBIGNUM), + ASN1_SIMPLE(RSA, iqmp, CBIGNUM) } ASN1_SEQUENCE_END_cb(RSA, RSAPrivateKey) diff --git a/crypto/rsa/rsa_gen.c b/crypto/rsa/rsa_gen.c index e81be75e44..e40186aced 100644 --- a/crypto/rsa/rsa_gen.c +++ b/crypto/rsa/rsa_gen.c @@ -117,19 +117,19 @@ static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value, /* We need the RSA components non-NULL */ if (!rsa->n && ((rsa->n = BN_new()) == NULL)) goto err; - if (!rsa->d && ((rsa->d = BN_new()) == NULL)) + if (!rsa->d && ((rsa->d = BN_secure_new()) == NULL)) goto err; if (!rsa->e && ((rsa->e = BN_new()) == NULL)) goto err; - if (!rsa->p && ((rsa->p = BN_new()) == NULL)) + if (!rsa->p && ((rsa->p = BN_secure_new()) == NULL)) goto err; - if (!rsa->q && ((rsa->q = BN_new()) == NULL)) + if (!rsa->q && ((rsa->q = BN_secure_new()) == NULL)) goto err; - if (!rsa->dmp1 && ((rsa->dmp1 = BN_new()) == NULL)) + if (!rsa->dmp1 && ((rsa->dmp1 = BN_secure_new()) == NULL)) goto err; - if (!rsa->dmq1 && ((rsa->dmq1 = BN_new()) == NULL)) + if (!rsa->dmq1 && ((rsa->dmq1 = BN_secure_new()) == NULL)) goto err; - if (!rsa->iqmp && ((rsa->iqmp = BN_new()) == NULL)) + if (!rsa->iqmp && ((rsa->iqmp = BN_secure_new()) == NULL)) goto err; BN_copy(rsa->e, e_value); diff --git a/crypto/sec_mem.c b/crypto/sec_mem.c new file mode 100644 index 0000000000..ea9523b2b9 --- /dev/null +++ b/crypto/sec_mem.c @@ -0,0 +1,513 @@ +/* + * Copyright 2004-2014, Akamai Technologies. All Rights Reserved. + * This file is distributed under the terms of the OpenSSL license. + */ + +/* + * This file is in two halves. The first half implements the public API + * to be used by external consumers, and to be used by OpenSSL to store + * data in a "secure arena." The second half implements the secure arena. + * For details on that implementation, see below (look for uppercase + * "SECURE HEAP IMPLEMENTATION"). + */ +#include +#include +#include +#include +#include +#include + +#if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_UNIX) +# define IMPLEMENTED +# include +# include +#endif + +#define LOCK() CRYPTO_w_lock(CRYPTO_LOCK_MALLOC) +#define UNLOCK() CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC) +#define CLEAR(p, s) OPENSSL_cleanse(p, s) +#define PAGE_SIZE 4096 + +#ifdef IMPLEMENTED +size_t secure_mem_used; + +static int secure_mem_initialized; +static int too_late; + +/* + * These are the functions that must be implemented by a secure heap (sh). + */ +static int sh_init(size_t size, int minsize); +static char *sh_malloc(size_t size); +static void sh_free(char *ptr); +static void sh_done(void); +static int sh_actual_size(char *ptr); +static int sh_allocated(const char *ptr); +#endif + +int CRYPTO_secure_malloc_init(size_t size, int minsize) +{ +#ifdef IMPLEMENTED + int ret = 0; + + if (too_late) + return ret; + LOCK(); + OPENSSL_assert(!secure_mem_initialized); + if (!secure_mem_initialized) { + ret = sh_init(size, minsize); + secure_mem_initialized = 1; + } + UNLOCK(); + return ret; +#else + return 0; +#endif /* IMPLEMENTED */ +} + +void CRYPTO_secure_malloc_done() +{ +#ifdef IMPLEMENTED + LOCK(); + sh_done(); + secure_mem_initialized = 0; + UNLOCK(); +#endif /* IMPLEMENTED */ +} + +int CRYPTO_secure_malloc_initialized() +{ +#ifdef IMPLEMENTED + return secure_mem_initialized; +#else + return 0; +#endif /* IMPLEMENTED */ +} + +void *CRYPTO_secure_malloc(int num, const char *file, int line) +{ +#ifdef IMPLEMENTED + void *ret; + size_t actual_size; + + if (!secure_mem_initialized) { + too_late = 1; + return CRYPTO_malloc(num, file, line); + } + LOCK(); + ret = sh_malloc(num); + actual_size = ret ? sh_actual_size(ret) : 0; + secure_mem_used += actual_size; + UNLOCK(); + return ret; +#else + return CRYPTO_malloc(num, file, line); +#endif /* IMPLEMENTED */ +} + +void CRYPTO_secure_free(void *ptr) +{ +#ifdef IMPLEMENTED + size_t actual_size; + + if (ptr == NULL) + return; + if (!secure_mem_initialized) { + CRYPTO_free(ptr); + return; + } + LOCK(); + actual_size = sh_actual_size(ptr); + CLEAR(ptr, actual_size); + secure_mem_used -= actual_size; + sh_free(ptr); + UNLOCK(); +#else + CRYPTO_free(ptr); +#endif /* IMPLEMENTED */ +} + +int CRYPTO_secure_allocated(const void *ptr) +{ +#ifdef IMPLEMENTED + int ret; + + if (!secure_mem_initialized) + return 0; + LOCK(); + ret = sh_allocated(ptr); + UNLOCK(); + return ret; +#else + return 0; +#endif /* IMPLEMENTED */ +} + +/* END OF PAGE ... + + ... START OF PAGE */ + +/* + * SECURE HEAP IMPLEMENTATION + */ +#ifdef IMPLEMENTED + + +/* + * The implementation provided here uses a fixed-sized mmap() heap, + * which is locked into memory, not written to core files, and protected + * on either side by an unmapped page, which will catch pointer overruns + * (or underruns) and an attempt to read data out of the secure heap. + * Free'd memory is zero'd or otherwise cleansed. + * + * This is a pretty standard buddy allocator. We keep areas in a multiple + * of "sh.minsize" units. The freelist and bitmaps are kept separately, + * so all (and only) data is kept in the mmap'd heap. + * + * This code assumes eight-bit bytes. The numbers 3 and 7 are all over the + * place. + */ + +# define TESTBIT(t, b) (t[(b) >> 3] & (1 << ((b) & 7))) +# define SETBIT(t, b) (t[(b) >> 3] |= (1 << ((b) & 7))) +# define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(1 << ((b) & 7)))) + +#define WITHIN_ARENA(p) \ + ((char*)(p) >= sh.arena && (char*)(p) < &sh.arena[sh.arena_size]) +#define WITHIN_FREELIST(p) \ + ((char*)(p) >= (char*)sh.freelist && (char*)(p) < (char*)&sh.freelist[sh.freelist_size]) + + +typedef struct sh_list_st +{ + struct sh_list_st *next; + struct sh_list_st **p_next; +} SH_LIST; + +typedef struct sh_st +{ + char* map_result; + size_t map_size; + char *arena; + int arena_size; + char **freelist; + int freelist_size; + int minsize; + unsigned char *bittable; + unsigned char *bitmalloc; + int bittable_size; /* size in bits */ +} SH; + +static SH sh; + +static int sh_getlist(char *ptr) +{ + int list = sh.freelist_size - 1; + int bit = (sh.arena_size + ptr - sh.arena) / sh.minsize; + + for (; bit; bit >>= 1, list--) { + if (TESTBIT(sh.bittable, bit)) + break; + OPENSSL_assert((bit & 1) == 0); + } + + return list; +} + + +static int sh_testbit(char *ptr, int list, unsigned char *table) +{ + int bit; + + OPENSSL_assert(list >= 0 && list < sh.freelist_size); + OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0); + bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list)); + OPENSSL_assert(bit > 0 && bit < sh.bittable_size); + return TESTBIT(table, bit); +} + +static void sh_clearbit(char *ptr, int list, unsigned char *table) +{ + int bit; + + OPENSSL_assert(list >= 0 && list < sh.freelist_size); + OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0); + bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list)); + OPENSSL_assert(bit > 0 && bit < sh.bittable_size); + OPENSSL_assert(TESTBIT(table, bit)); + CLEARBIT(table, bit); +} + +static void sh_setbit(char *ptr, int list, unsigned char *table) +{ + int bit; + + OPENSSL_assert(list >= 0 && list < sh.freelist_size); + OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0); + bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list)); + OPENSSL_assert(bit > 0 && bit < sh.bittable_size); + OPENSSL_assert(!TESTBIT(table, bit)); + SETBIT(table, bit); +} + +static void sh_add_to_list(char **list, char *ptr) +{ + SH_LIST *temp; + + OPENSSL_assert(WITHIN_FREELIST(list)); + OPENSSL_assert(WITHIN_ARENA(ptr)); + + temp = (SH_LIST *)ptr; + temp->next = *(SH_LIST **)list; + OPENSSL_assert(temp->next == NULL || WITHIN_ARENA(temp->next)); + temp->p_next = (SH_LIST **)list; + + if (temp->next != NULL) { + OPENSSL_assert((char **)temp->next->p_next == list); + temp->next->p_next = &(temp->next); + } + + *list = ptr; +} + +static void sh_remove_from_list(char *ptr, char *list) +{ + SH_LIST *temp, *temp2; + + temp = (SH_LIST *)ptr; + if (temp->next != NULL) + temp->next->p_next = temp->p_next; + *temp->p_next = temp->next; + if (temp->next == NULL) + return; + + temp2 = temp->next; + OPENSSL_assert(WITHIN_FREELIST(temp2->p_next) || WITHIN_ARENA(temp2->p_next)); +} + + +static int sh_init(size_t size, int minsize) +{ + int i, ret; + size_t pgsize; + size_t aligned; + + memset(&sh, 0, sizeof sh); + + /* make sure size and minsize are powers of 2 */ + OPENSSL_assert(size > 0); + OPENSSL_assert((size & (size - 1)) == 0); + OPENSSL_assert(minsize > 0); + OPENSSL_assert((minsize & (minsize - 1)) == 0); + if (size <= 0 || (size & (size - 1)) != 0) + goto err; + if (minsize <= 0 || (minsize & (minsize - 1)) != 0) + goto err; + + sh.arena_size = size; + sh.minsize = minsize; + sh.bittable_size = (sh.arena_size / sh.minsize) * 2; + + sh.freelist_size = -1; + for (i = sh.bittable_size; i; i >>= 1) + sh.freelist_size++; + + sh.freelist = OPENSSL_malloc(sh.freelist_size * sizeof (char *)); + OPENSSL_assert(sh.freelist != NULL); + if (sh.freelist == NULL) + goto err; + memset(sh.freelist, 0, sh.freelist_size * sizeof (char *)); + + sh.bittable = OPENSSL_malloc(sh.bittable_size >> 3); + OPENSSL_assert(sh.bittable != NULL); + if (sh.bittable == NULL) + goto err; + memset(sh.bittable, 0, sh.bittable_size >> 3); + + sh.bitmalloc = OPENSSL_malloc(sh.bittable_size >> 3); + OPENSSL_assert(sh.bitmalloc != NULL); + if (sh.bitmalloc == NULL) + goto err; + memset(sh.bitmalloc, 0, sh.bittable_size >> 3); + + /* Allocate space for heap, and two extra pages as guards */ +#ifdef _SC_PAGE_SIZE + pgsize = (size_t)sysconf(_SC_PAGE_SIZE); +#else + pgsize = PAGE_SIZE; +#endif + sh.map_size = pgsize + sh.arena_size + pgsize; + sh.map_result = mmap(NULL, sh.map_size, + PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); + OPENSSL_assert(sh.map_result != MAP_FAILED); + if (sh.map_result == MAP_FAILED) + goto err; + sh.arena = (char *)(sh.map_result + pgsize); + sh_setbit(sh.arena, 0, sh.bittable); + sh_add_to_list(&sh.freelist[0], sh.arena); + + /* Now try to add guard pages and lock into memory. */ + ret = 1; + + /* Starting guard is already aligned from mmap. */ + if (mprotect(sh.map_result, pgsize, PROT_NONE) < 0) + ret = 2; + + /* Ending guard page - need to round up to page boundary */ + aligned = (pgsize + sh.arena_size + (pgsize - 1)) & ~(pgsize - 1); + if (mprotect(sh.map_result + aligned, pgsize, PROT_NONE) < 0) + ret = 2; + + if (mlock(sh.arena, sh.arena_size) < 0) + ret = 2; +#ifdef MADV_DONTDUMP + if (madvise(sh.arena, sh.arena_size, MADV_DONTDUMP) < 0) + ret = 2; +#endif + + return ret; + + err: + sh_done(); + return 0; +} + +static void sh_done() +{ + OPENSSL_free(sh.freelist); + OPENSSL_free(sh.bittable); + OPENSSL_free(sh.bitmalloc); + if (sh.map_result != NULL && sh.map_size) + munmap(sh.map_result, sh.map_size); + memset(&sh, 0, sizeof sh); +} + +static int sh_allocated(const char *ptr) +{ + return WITHIN_ARENA(ptr) ? 1 : 0; +} + +static char *sh_find_my_buddy(char *ptr, int list) +{ + int bit; + char *chunk = NULL; + + bit = (1 << list) + (ptr - sh.arena) / (sh.arena_size >> list); + bit ^= 1; + + if (TESTBIT(sh.bittable, bit) && !TESTBIT(sh.bitmalloc, bit)) + chunk = sh.arena + ((bit & ((1 << list) - 1)) * (sh.arena_size >> list)); + + return chunk; +} + +static char *sh_malloc(size_t size) +{ + int list, slist; + size_t i; + char *chunk; + + list = sh.freelist_size - 1; + for (i = sh.minsize; i < size; i <<= 1) + list--; + if (list < 0) + return NULL; + + /* try to find a larger entry to split */ + for (slist = list; slist >= 0; slist--) + if (sh.freelist[slist] != NULL) + break; + if (slist < 0) + return NULL; + + /* split larger entry */ + while (slist != list) { + char *temp = sh.freelist[slist]; + + /* remove from bigger list */ + OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc)); + sh_clearbit(temp, slist, sh.bittable); + sh_remove_from_list(temp, sh.freelist[slist]); + OPENSSL_assert(temp != sh.freelist[slist]); + + /* done with bigger list */ + slist++; + + /* add to smaller list */ + OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc)); + sh_setbit(temp, slist, sh.bittable); + sh_add_to_list(&sh.freelist[slist], temp); + OPENSSL_assert(sh.freelist[slist] == temp); + + /* split in 2 */ + temp += sh.arena_size >> slist; + OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc)); + sh_setbit(temp, slist, sh.bittable); + sh_add_to_list(&sh.freelist[slist], temp); + OPENSSL_assert(sh.freelist[slist] == temp); + + OPENSSL_assert(temp-(sh.arena_size >> slist) == sh_find_my_buddy(temp, slist)); + } + + /* peel off memory to hand back */ + chunk = sh.freelist[list]; + OPENSSL_assert(sh_testbit(chunk, list, sh.bittable)); + sh_setbit(chunk, list, sh.bitmalloc); + sh_remove_from_list(chunk, sh.freelist[list]); + + OPENSSL_assert(WITHIN_ARENA(chunk)); + + return chunk; +} + +static void sh_free(char *ptr) +{ + int list; + char *buddy; + + if (ptr == NULL) + return; + OPENSSL_assert(WITHIN_ARENA(ptr)); + if (!WITHIN_ARENA(ptr)) + return; + + list = sh_getlist(ptr); + OPENSSL_assert(sh_testbit(ptr, list, sh.bittable)); + sh_clearbit(ptr, list, sh.bitmalloc); + sh_add_to_list(&sh.freelist[list], ptr); + + /* Try to coalesce two adjacent free areas. */ + while ((buddy = sh_find_my_buddy(ptr, list)) != NULL) { + OPENSSL_assert(ptr == sh_find_my_buddy(buddy, list)); + OPENSSL_assert(ptr != NULL); + OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc)); + sh_clearbit(ptr, list, sh.bittable); + sh_remove_from_list(ptr, sh.freelist[list]); + OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc)); + sh_clearbit(buddy, list, sh.bittable); + sh_remove_from_list(buddy, sh.freelist[list]); + + list--; + + if (ptr > buddy) + ptr = buddy; + + OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc)); + sh_setbit(ptr, list, sh.bittable); + sh_add_to_list(&sh.freelist[list], ptr); + OPENSSL_assert(sh.freelist[list] == ptr); + } +} + +static int sh_actual_size(char *ptr) +{ + int list; + + OPENSSL_assert(WITHIN_ARENA(ptr)); + if (!WITHIN_ARENA(ptr)) + return 0; + list = sh_getlist(ptr); + OPENSSL_assert(sh_testbit(ptr, list, sh.bittable)); + return sh.arena_size / (1 << list); +} +#endif /* IMPLEMENTED */ diff --git a/doc/crypto/BIO_s_mem.pod b/doc/crypto/BIO_s_mem.pod index 8f85e0dcee..1aa7e6ebf9 100644 --- a/doc/crypto/BIO_s_mem.pod +++ b/doc/crypto/BIO_s_mem.pod @@ -10,6 +10,7 @@ BIO_get_mem_ptr, BIO_new_mem_buf - memory BIO #include BIO_METHOD * BIO_s_mem(void); + BIO_METHOD * BIO_s_secmem(void); BIO_set_mem_eof_return(BIO *b,int v) long BIO_get_mem_data(BIO *b, char **pp) @@ -26,6 +27,9 @@ A memory BIO is a source/sink BIO which uses memory for its I/O. Data written to a memory BIO is stored in a BUF_MEM structure which is extended as appropriate to accommodate the stored data. +BIO_s_secmem() is like BIO_s_mem() except that the secure heap is used +for buffer storage. + Any data written to a memory BIO can be recalled by reading from it. Unless the memory BIO is read only any data read from it is deleted from the BIO. @@ -79,6 +83,9 @@ read in small chunks the operation can be very slow. The use of a read only memory BIO avoids this problem. If the BIO must be read write then adding a buffering BIO to the chain will speed up the process. +Calling BIO_set_mem_buf() on a BIO created with BIO_new_secmem() will +give undefined results, including perhaps a program crash. + =head1 BUGS There should be an option to set the maximum size of a memory BIO. diff --git a/doc/crypto/BN_CTX_new.pod b/doc/crypto/BN_CTX_new.pod index 5da8433637..958e551467 100644 --- a/doc/crypto/BN_CTX_new.pod +++ b/doc/crypto/BN_CTX_new.pod @@ -2,7 +2,7 @@ =head1 NAME -BN_CTX_new, BN_CTX_init, BN_CTX_free - allocate and free BN_CTX structures +BN_CTX_new, BN_CTX_secure_new, BN_CTX_init, BN_CTX_free - allocate and free BN_CTX structures =head1 SYNOPSIS @@ -10,6 +10,8 @@ BN_CTX_new, BN_CTX_init, BN_CTX_free - allocate and free BN_CTX structures BN_CTX *BN_CTX_new(void); + BN_CTX *BN_CTX_secure_new(void); + void BN_CTX_free(BN_CTX *c); =head1 DESCRIPTION @@ -19,8 +21,10 @@ library functions. Since dynamic memory allocation to create Bs is rather expensive when used in conjunction with repeated subroutine calls, the B structure is used. -BN_CTX_new() allocates and initializes a B -structure. +BN_CTX_new() allocates and initializes a B structure. +BN_CTX_secure_new() allocates and initializes a B structure +but uses the secure heap (see L) to hold the +Bs. BN_CTX_free() frees the components of the B, and if it was created by BN_CTX_new(), also the structure itself. @@ -31,8 +35,9 @@ If B is NULL, nothing is done. =head1 RETURN VALUES -BN_CTX_new() returns a pointer to the B. If the allocation fails, -it returns B and sets an error code that can be obtained by +BN_CTX_new() and BN_CTX_secure_new() return a pointer to the B. +If the allocation fails, +they return B and sets an error code that can be obtained by L. BN_CTX_free() has no return values. diff --git a/doc/crypto/CRYPTO_secure_malloc.pod b/doc/crypto/CRYPTO_secure_malloc.pod new file mode 100644 index 0000000000..a3b416ed08 --- /dev/null +++ b/doc/crypto/CRYPTO_secure_malloc.pod @@ -0,0 +1,91 @@ +=pod + +=head1 NAME + +CRYPTO_secure_malloc_init, CRYPTO_secure_malloc_done, OPENSSL_secure_malloc, OPENSSL_secure_free, OPENSSL_secure_allocated - use secure heap storage + +=head1 SYNOPSIS + + #include + + int CRYPTO_secure_malloc_init(size_t size, int minsize); + + int CRYPTO_secure_malloc_initialized(); + + void CRYPTO_secure_malloc_done(); + + void *OPENSSL_secure_malloc(int num); + + void OPENSSL_secure_free(void* ptr); + + int OPENSSL_secure_allocated(const void* ptr); + +=head1 DESCRIPTION + +In order to help protect applications (particularly long-running servers) +from pointer overruns or underruns that could return arbitrary data from +the program's dynamic memory area, where keys and other sensitive +information might be stored, OpenSSL supports the concept of a "secure heap." +The level and type of security guarantees depend on the operating system. +It is a good idea to review the code and see if it addresses your +threat model and concerns. + +If a secure heap is used, then private key B values are stored there. +This protects long-term storage of private keys, but will not necessarily +put all intermediate values and computations there. + +B creates the secure heap, with the specified +C in bytes. The C parameter is the minimum size to +allocate from the heap. Both C and C must be a power +of two. It is an error to call this after any B +calls have been made. + +B indicates whether or not the secure +heap as been initialized and is available. + +B releases the heap and makes the memory unavailable +to the process. It can take noticeably long to complete. + +B allocates C bytes from the heap. +If B is not called, this is equivalent to +calling B. + +B releases the memory at C back to the heap. +It must be called with a value previously obtained from +B. +If B is not called, this is equivalent to +calling B. + +B tells whether or not a pointer is within +the secure heap. + +=head1 RETURN VALUES + +B returns 0 on failure, 1 if successful, +and 2 if successful but the heap could not be protected by memory +mapping. + +B returns 1 if the secure heap is +available (that is, if B has been called, +but B has not) or 0 if not. + +B returns a pointer into the secure heap of +the requested size, or C if memory could not be allocated. + +B returns 1 if the pointer is in the +the secure heap, or 0 if not. + +B and B +return no values. + +=head1 SEE ALSO + +L, +L + +=head1 HISTORY + +These functions were contributed to the OpenSSL project by +Akamai Technologies in April, 2014. + +=cut diff --git a/doc/crypto/bio.pod b/doc/crypto/bio.pod index f9239226ff..9debe4f1b6 100644 --- a/doc/crypto/bio.pod +++ b/doc/crypto/bio.pod @@ -49,6 +49,7 @@ L, L, L, L, L, L, L, L, +L, L, L, L, L diff --git a/doc/crypto/bn.pod b/doc/crypto/bn.pod index b52916bada..ab809f95e4 100644 --- a/doc/crypto/bn.pod +++ b/doc/crypto/bn.pod @@ -14,6 +14,7 @@ bn - multiprecision integer arithmetics void BN_clear_free(BIGNUM *a); BN_CTX *BN_CTX_new(void); + BN_CTX *BN_CTX_secure_new(void); void BN_CTX_free(BN_CTX *c); BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); diff --git a/doc/crypto/buffer.pod b/doc/crypto/buffer.pod index 781f5b11ee..3804c5649b 100644 --- a/doc/crypto/buffer.pod +++ b/doc/crypto/buffer.pod @@ -11,6 +11,10 @@ character arrays structure BUF_MEM *BUF_MEM_new(void); + #define BUF_MEM_FLAG_SECURE + + BUF_MEM * BUF_MEM_new_ex(unsigned long flags); + void BUF_MEM_free(BUF_MEM *a); int BUF_MEM_grow(BUF_MEM *str, int len); @@ -37,6 +41,10 @@ and one "miscellaneous" function. BUF_MEM_new() allocates a new buffer of zero size. +BUF_MEM_new_ex() allocates a buffer with the specified flags. +The flag B specifies that the B pointer +should be allocated on the secure heap; see L. + BUF_MEM_free() frees up an already existing buffer. The data is zeroed before freeing up in case the buffer contains sensitive data. @@ -63,11 +71,15 @@ BUF_MEM_grow() returns zero on error or the new size (i.e. B). =head1 SEE ALSO -L +L, +L. =head1 HISTORY BUF_MEM_new(), BUF_MEM_free() and BUF_MEM_grow() are available in all versions of SSLeay and OpenSSL. BUF_strdup() was added in SSLeay 0.8. +BUF_MEM_new_ex() was contributed to OpenSSL by Akamai Technologies +in May, 2014. + =cut diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 7fe88ec4ef..2da93bdf0a 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -670,6 +670,7 @@ long BIO_debug_callback(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret); BIO_METHOD *BIO_s_mem(void); +BIO_METHOD *BIO_s_secmem(void); BIO *BIO_new_mem_buf(void *buf, int len); BIO_METHOD *BIO_s_socket(void); BIO_METHOD *BIO_s_connect(void); diff --git a/include/openssl/bn.h b/include/openssl/bn.h index 44f17374f2..0fcf843314 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -268,6 +268,7 @@ extern "C" { * BN_mod_inverse() will call BN_mod_inverse_no_branch. */ # define BN_FLG_CONSTTIME 0x04 +# define BN_FLG_SECURE 0x08 # ifdef OPENSSL_USE_DEPRECATED /* deprecated name for the flag */ @@ -349,6 +350,7 @@ void BN_zero_ex(BIGNUM *a); const BIGNUM *BN_value_one(void); char *BN_options(void); BN_CTX *BN_CTX_new(void); +BN_CTX *BN_CTX_secure_new(void); void BN_CTX_free(BN_CTX *c); void BN_CTX_start(BN_CTX *ctx); BIGNUM *BN_CTX_get(BN_CTX *ctx); @@ -361,6 +363,7 @@ int BN_num_bits(const BIGNUM *a); int BN_num_bits_word(BN_ULONG l); int BN_security_bits(int L, int N); BIGNUM *BN_new(void); +BIGNUM *BN_secure_new(void); void BN_clear_free(BIGNUM *a); BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); void BN_swap(BIGNUM *a, BIGNUM *b); diff --git a/include/openssl/buffer.h b/include/openssl/buffer.h index 672c06b395..af30a90b86 100644 --- a/include/openssl/buffer.h +++ b/include/openssl/buffer.h @@ -78,9 +78,13 @@ struct buf_mem_st { size_t length; /* current number of bytes */ char *data; size_t max; /* size of buffer */ + unsigned long flags; }; +# define BUF_MEM_FLAG_SECURE 0x01 + BUF_MEM *BUF_MEM_new(void); +BUF_MEM *BUF_MEM_new_ex(unsigned long flags); void BUF_MEM_free(BUF_MEM *a); size_t BUF_MEM_grow(BUF_MEM *str, size_t len); size_t BUF_MEM_grow_clean(BUF_MEM *str, size_t len); diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h index f05084f8c1..1bda645660 100644 --- a/include/openssl/crypto.h +++ b/include/openssl/crypto.h @@ -491,12 +491,12 @@ void (*CRYPTO_get_dynlock_destroy_callback(void)) (struct CRYPTO_dynlock_value int CRYPTO_set_mem_functions(void *(*m) (size_t), void *(*r) (void *, size_t), void (*f) (void *)); int CRYPTO_set_locked_mem_functions(void *(*m) (size_t), - void (*free_func) (void *)); + void (*f) (void *)); int CRYPTO_set_mem_ex_functions(void *(*m) (size_t, const char *, int), void *(*r) (void *, size_t, const char *, int), void (*f) (void *)); int CRYPTO_set_locked_mem_ex_functions(void *(*m) (size_t, const char *, int), - void (*free_func) (void *)); + void (*f) (void *)); int CRYPTO_set_mem_debug_functions(void (*m) (void *, int, const char *, int, int), void (*r) (void *, void *, int, @@ -532,6 +532,25 @@ void *CRYPTO_realloc_clean(void *addr, int old_num, int num, const char *file, int line); void *CRYPTO_remalloc(void *addr, int num, const char *file, int line); +# define OPENSSL_secure_malloc(num) \ + CRYPTO_secure_malloc((int)num,__FILE__,__LINE__) +# define OPENSSL_secure_free(addr) \ + CRYPTO_secure_free(addr) + +int CRYPTO_secure_malloc_init(size_t sz, int minsize); +void CRYPTO_secure_malloc_done(void); +void *CRYPTO_secure_malloc(int num, const char *file, int line); +void CRYPTO_secure_free(void *ptr); +int CRYPTO_secure_allocated(const void *ptr); +int CRYPTO_secure_malloc_initialized(void); + +int CRYPTO_set_secure_mem_functions(void *(*m)(size_t), void (*f)(void *)); +int CRYPTO_set_secure_mem_ex_functions(void *(*m)(size_t,const char *,int), + void (*f)(void *)); +void CRYPTO_get_secure_mem_functions(void *(**m)(size_t), void (**f)(void *)); +void CRYPTO_get_secure_mem_ex_functions(void *(**m)(size_t,const char *,int), + void (**f)(void *)); + void OPENSSL_cleanse(void *ptr, size_t len); void CRYPTO_set_mem_debug_options(long bits); diff --git a/test/Makefile b/test/Makefile index 343c21af28..508c53b6fd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -63,6 +63,7 @@ EVPEXTRATEST=evp_extra_test P5_CRPT2_TEST= p5_crpt2_test IGETEST= igetest JPAKETEST= jpaketest +SECMEMTEST= secmemtest SRPTEST= srptest V3NAMETEST= v3nametest HEARTBEATTEST= heartbeat_test @@ -80,7 +81,8 @@ EXE= $(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT) $(ECDSATEST)$(EXE_EXT) $(ECDHTEST) $(BFTEST)$(EXE_EXT) $(CASTTEST)$(EXE_EXT) $(SSLTEST)$(EXE_EXT) \ $(EXPTEST)$(EXE_EXT) $(DSATEST)$(EXE_EXT) $(RSATEST)$(EXE_EXT) \ $(EVPTEST)$(EXE_EXT) $(EVPEXTRATEST)$(EXE_EXT) $(IGETEST)$(EXE_EXT) \ - $(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) \ + $(JPAKETEST)$(EXE_EXT) $(SECMEMTEST)$(EXE_EXT) \ + $(SRPTEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) \ $(HEARTBEATTEST)$(EXE_EXT) $(P5_CRPT2_TEST)$(EXE_EXT) \ $(CONSTTIMETEST)$(EXE_EXT) @@ -145,7 +147,8 @@ alltests: \ test_enc test_x509 test_rsa test_crl test_sid \ test_gen test_req test_pkcs7 test_verify test_dh test_dsa \ test_ss test_ca test_engine test_evp test_evp_extra test_ssl test_tsa \ - test_ige test_jpake test_srp test_cms test_v3name test_ocsp \ + test_ige test_jpake test_secmem \ + test_srp test_cms test_v3name test_ocsp \ test_gost2814789 test_heartbeat test_p5_crpt2 \ test_constant_time @@ -372,6 +375,10 @@ test_cms: ../apps/openssl$(EXE_EXT) cms-test.pl smcont.txt @echo $(START) $@ $(PERL) cms-test.pl +test_secmem: $(SECMEMTEST)$(EXE_EXT) + @echo $(START) $@ + ../util/shlib_wrap.sh ./secmemtest + test_srp: $(SRPTEST)$(EXE_EXT) @echo $(START) $@ ../util/shlib_wrap.sh ./srptest @@ -564,6 +571,9 @@ $(IGETEST)$(EXE_EXT): $(IGETEST).o $(DLIBCRYPTO) $(JPAKETEST)$(EXE_EXT): $(JPAKETEST).o $(DLIBCRYPTO) @target=$(JPAKETEST); $(BUILD_CMD) +$(SECMEMTEST)$(EXE_EXT): $(SECMEMTEST).o $(DLIBCRYPTO) + @target=$(SECMEMTEST); $(BUILD_CMD) + $(SRPTEST)$(EXE_EXT): $(SRPTEST).o $(DLIBCRYPTO) @target=$(SRPTEST); $(BUILD_CMD) diff --git a/test/secmemtest.c b/test/secmemtest.c new file mode 100644 index 0000000000..0ec3b92571 --- /dev/null +++ b/test/secmemtest.c @@ -0,0 +1,34 @@ + +#include + +int main(int argc, char **argv) +{ +#if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_UNIX) + char *p = NULL, *q = NULL; + + if (!CRYPTO_secure_malloc_init(4096, 32)) { + perror("failed"); + return 1; + } + p = OPENSSL_secure_malloc(20); + if (!CRYPTO_secure_allocated(p)) { + perror("failed 1"); + return 1; + } + q = OPENSSL_malloc(20); + if (CRYPTO_secure_allocated(q)) { + perror("failed 1"); + return 1; + } + CRYPTO_secure_free(p); + CRYPTO_free(q); + CRYPTO_secure_malloc_done(); +#else + /* Should fail. */ + if (CRYPTO_secure_malloc_init(4096, 32)) { + perror("failed"); + return 1; + } +#endif + return 0; +} diff --git a/util/libeay.num b/util/libeay.num index edeb50d34f..9933499980 100755 --- a/util/libeay.num +++ b/util/libeay.num @@ -4573,3 +4573,17 @@ PKCS5_pbe2_set_scrypt 4931 EXIST::FUNCTION: PKCS8_set0_pbe 4932 EXIST::FUNCTION: DH_bits 4933 EXIST::FUNCTION:DH RSA_bits 4934 EXIST::FUNCTION:RSA +CRYPTO_set_secure_mem_ex_functions 4935 EXIST::FUNCTION: +CRYPTO_secure_allocated 4936 EXIST::FUNCTION: +BN_CTX_secure_new 4937 EXIST::FUNCTION: +CRYPTO_secure_malloc 4938 EXIST::FUNCTION: +CRYPTO_secure_malloc_done 4939 EXIST::FUNCTION: +BUF_MEM_new_ex 4940 EXIST::FUNCTION: +CRYPTO_secure_malloc_initialized 4941 EXIST::FUNCTION: +CRYPTO_secure_malloc_init 4942 EXIST::FUNCTION: +CRYPTO_get_secure_mem_functions 4943 EXIST::FUNCTION: +BN_secure_new 4944 EXIST::FUNCTION: +CRYPTO_secure_free 4945 EXIST::FUNCTION: +BIO_s_secmem 4946 EXIST::FUNCTION: +CRYPTO_get_secure_mem_ex_functions 4947 EXIST::FUNCTION: +CRYPTO_set_secure_mem_functions 4948 EXIST::FUNCTION: -- 2.25.1