Skip to contentSkip to conversation

evmctl: Use secure heap for private keys and passwords

PATCH In review Vitaly Chikunov 3 participants 9 messages Sep 4, 2021 – Sep 13, 2021 Keys-trusted

lore
Share & tools
lore
#0 lore raw

PATCH v4 1/1 evmctl: Use secure heap for private keys and passwords

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

 src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 85 insertions(+), 12 deletions(-)
src/evmctl.c +85 -12
index 5f7c2b8..7bd20f8 100644
@@ -59,6 +59,7 @@
 #include <assert.h>
 
 #include <openssl/asn1.h>
+#include <openssl/crypto.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
@@ -165,6 +166,24 @@ struct tpm_bank_info {
 static char *pcrfile[MAX_PCRFILE];
 static unsigned npcrfile;
 
+#if OPENSSL_VERSION_NUMBER <= 0x10100000
+#warning Your OpenSSL version is too old to have OPENSSL_secure_malloc, \
+	falling back to use plain OPENSSL_malloc.
+#define OPENSSL_secure_malloc	  OPENSSL_malloc
+#define OPENSSL_secure_free	  OPENSSL_free
+/*
+ * Secure heap memory automatically cleared on free, but
+ * OPENSSL_secure_clear_free will be used in case of fallback
+ * to plain OPENSSL_malloc.
+ */
+#define OPENSSL_secure_clear_free OPENSSL_clear_free
+#define OPENSSL_clear_free(ptr, num)		\
+	do {					\
+		OPENSSL_cleanse(ptr, num);	\
+		OPENSSL_free(ptr);		\
+	} while (0)
+#endif
+
 static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
 {
 	FILE *fp;
@@ -1072,6 +1091,7 @@ static int cmd_setxattr_ima(struct command *cmd)
 
 #define MAX_KEY_SIZE 128
 
+/* This function is debugging tool and should not be used for real private data. */
 static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
 {
         const EVP_MD *md;
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
 
 };
 
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */
+static char *optarg_password(char *optarg)
+{
+	size_t len;
+	char *keypass;
+
+	if (!optarg)
+		return NULL;
+	len = strlen(optarg);
+	keypass = OPENSSL_secure_malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		log_err("OPENSSL_secure_malloc(%zu) failed\n", len + 1);
+	/*
+	 * This memset does not add real security, just increases
+	 * the chance of password being obscured in ps output.
+	 */
+	memset(optarg, 'X', len);
+	return keypass;
+}
+
+/* Read from TTY into secure heap. */
 static char *get_password(void)
 {
 	struct termios flags, tmp_flags;
 	char *password, *pwd;
-	int passlen = 64;
+	const int passlen = 64;
 
-	password = malloc(passlen);
+	password = OPENSSL_secure_malloc(passlen);
 	if (!password) {
-		perror("malloc");
+		log_err("OPENSSL_secure_malloc(%u) failed\n", passlen);
 		return NULL;
 	}
 
@@ -2614,8 +2660,8 @@ static char *get_password(void)
 	tmp_flags.c_lflag |= ECHONL;
 
 	if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
-		perror("tcsetattr");
-		free(password);
+		log_err("tcsetattr: %s\n", strerror(errno));
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2624,13 +2670,15 @@ static char *get_password(void)
 
 	/* restore terminal */
 	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
-		perror("tcsetattr");
-		free(password);
-		return NULL;
+		log_err("tcsetattr: %s\n", strerror(errno));
+		/*
+		 * Password is already here, so there is no reason
+		 * to stop working on this petty error.
+		 */
 	}
 
 	if (pwd == NULL) {
-		free(password);
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
 	ENGINE *eng = NULL;
 	unsigned long keyid;
 	char *eptr;
+	char *keypass = NULL; /* @secure heap */
 
 #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
 	OPENSSL_init_crypto(
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
 #endif
 			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
 #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.
+	 * 'Heap size' 8192 is chosen to be big enough, so that any single key
+	 * data could fit. 'Heap minsize' 64 is just to be efficient for small
+	 * buffers.
+	 */
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");
+#endif
+
 	g_argv = argv;
 	g_argc = argc;
 
@@ -2682,10 +2742,18 @@ int main(int argc, char *argv[])
 			imaevm_params.hash_algo = optarg;
 			break;
 		case 'p':
+			if (keypass)
+				OPENSSL_secure_clear_free(keypass,
+							  strlen(keypass));
 			if (optarg)
-				imaevm_params.keypass = optarg;
+				keypass = optarg_password(optarg);
 			else
-				imaevm_params.keypass = get_password();
+				keypass = get_password();
+			if (!keypass) {
+				log_err("Cannot read password\n");
+				goto quit;
+			}
+			imaevm_params.keypass = keypass;
 			break;
 		case 'f':
 			sigfile = 1;
@@ -2841,7 +2909,9 @@ int main(int argc, char *argv[])
 		if (err < 0)
 			err = 125;
 	}
-
+quit:
+	if (keypass)
+		OPENSSL_secure_clear_free(keypass, strlen(keypass));
 	if (eng) {
 		ENGINE_finish(eng);
 		ENGINE_free(eng);
@@ -2849,6 +2919,9 @@ int main(int argc, char *argv[])
 		ENGINE_cleanup();
 #endif
 	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	CRYPTO_secure_malloc_done();
+#endif
 	ERR_free_strings();
 	EVP_cleanup();
 	BIO_free(NULL);
Signature · 1 line
2.29.3
#1 lore raw
Quoted 68 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

  src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
  1 file changed, 85 insertions(+), 12 deletions(-)
src/evmctl.c +20 -0
index 5f7c2b8..7bd20f8 100644
@@ -59,6 +59,7 @@
  #include <assert.h>
 
  #include <openssl/asn1.h>
+#include <openssl/crypto.h>
  #include <openssl/sha.h>
  #include <openssl/pem.h>
  #include <openssl/hmac.h>
@@ -165,6 +166,24 @@ struct tpm_bank_info {
  static char *pcrfile[MAX_PCRFILE];
  static unsigned npcrfile;
 
+#if OPENSSL_VERSION_NUMBER <= 0x10100000
+#warning Your OpenSSL version is too old to have OPENSSL_secure_malloc, \
+	falling back to use plain OPENSSL_malloc.
+#define OPENSSL_secure_malloc	  OPENSSL_malloc
+#define OPENSSL_secure_free	  OPENSSL_free
+/*
+ * Secure heap memory automatically cleared on free, but
+ * OPENSSL_secure_clear_free will be used in case of fallback
+ * to plain OPENSSL_malloc.
+ */
+#define OPENSSL_secure_clear_free OPENSSL_clear_free
+#define OPENSSL_clear_free(ptr, num)		\
+	do {					\
+		OPENSSL_cleanse(ptr, num);	\
+		OPENSSL_free(ptr);		\
+	} while (0)
+#endif
+
  static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
  {
  	FILE *fp;
@@ -1072,6 +1091,7 @@ static int cmd_setxattr_ima(struct command *cmd)
 
  #define MAX_KEY_SIZE 128
 
+/* This function is debugging tool and should not be used for real private data. */
  static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
  {
          const EVP_MD *md;

src/evmctl.c static struct option opts[] = { · line 2624

Quoted 9 lines
src/evmctl.c +6 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
 
  };
 
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */
+static char *optarg_password(char *optarg)
+{

Mimi applied my patch that takes the password from an environment variable to the next-testing branch. The code there (imaevm_params.keypass  = getenv("..."))would have to change as well calling this function here. Even though the man page of getenv says that 'The caller must take care not to modify this string, since that would change the environment of the process' we should be able to overwrite the env variable's password value just like here. Maybe for this purpose rename this function to dup_password() ?

Quoted 75 lines
src/evmctl.c +32 -9
+	size_t len;
+	char *keypass;
+
+	if (!optarg)
+		return NULL;
+	len = strlen(optarg);
+	keypass = OPENSSL_secure_malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		log_err("OPENSSL_secure_malloc(%zu) failed\n", len + 1);
+	/*
+	 * This memset does not add real security, just increases
+	 * the chance of password being obscured in ps output.
+	 */
+	memset(optarg, 'X', len);
+	return keypass;
+}
+
+/* Read from TTY into secure heap. */
  static char *get_password(void)
  {
  	struct termios flags, tmp_flags;
  	char *password, *pwd;
-	int passlen = 64;
+	const int passlen = 64;
 
-	password = malloc(passlen);
+	password = OPENSSL_secure_malloc(passlen);
  	if (!password) {
-		perror("malloc");
+		log_err("OPENSSL_secure_malloc(%u) failed\n", passlen);
  		return NULL;
  	}
 
@@ -2614,8 +2660,8 @@ static char *get_password(void)
  	tmp_flags.c_lflag |= ECHONL;
 
  	if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
-		perror("tcsetattr");
-		free(password);
+		log_err("tcsetattr: %s\n", strerror(errno));
+		OPENSSL_secure_free(password);
  		return NULL;
  	}
 
@@ -2624,13 +2670,15 @@ static char *get_password(void)
 
  	/* restore terminal */
  	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
-		perror("tcsetattr");
-		free(password);
-		return NULL;
+		log_err("tcsetattr: %s\n", strerror(errno));
+		/*
+		 * Password is already here, so there is no reason
+		 * to stop working on this petty error.
+		 */
  	}
 
  	if (pwd == NULL) {
-		free(password);
+		OPENSSL_secure_free(password);
  		return NULL;
  	}
 
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
  	ENGINE *eng = NULL;
  	unsigned long keyid;
  	char *eptr;
+	char *keypass = NULL; /* @secure heap */
 
  #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
  	OPENSSL_init_crypto(

src/evmctl.c int main(int argc, char *argv[]) · line 2705

Quoted 6 lines
src/evmctl.c +3 -0
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
  #endif
  			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
  #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.

Shouldn't the comparison then not include 0x10100000?

#if OPENSSL_VERSION_NUMBER >= 0x10100000

Quoted 44 lines
src/evmctl.c +21 -3
+	 * 'Heap size' 8192 is chosen to be big enough, so that any single key
+	 * data could fit. 'Heap minsize' 64 is just to be efficient for small
+	 * buffers.
+	 */
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");
+#endif
+
  	g_argv = argv;
  	g_argc = argc;
 
@@ -2682,10 +2742,18 @@ int main(int argc, char *argv[])
  			imaevm_params.hash_algo = optarg;
  			break;
  		case 'p':
+			if (keypass)
+				OPENSSL_secure_clear_free(keypass,
+							  strlen(keypass));
  			if (optarg)
-				imaevm_params.keypass = optarg;
+				keypass = optarg_password(optarg);
  			else
-				imaevm_params.keypass = get_password();
+				keypass = get_password();
+			if (!keypass) {
+				log_err("Cannot read password\n");
+				goto quit;
+			}
+			imaevm_params.keypass = keypass;
  			break;
  		case 'f':
  			sigfile = 1;
@@ -2841,7 +2909,9 @@ int main(int argc, char *argv[])
  		if (err < 0)
  			err = 125;
  	}
-
+quit:
+	if (keypass)
+		OPENSSL_secure_clear_free(keypass, strlen(keypass));
  	if (eng) {
  		ENGINE_finish(eng);
  		ENGINE_free(eng);

src/evmctl.c int main(int argc, char *argv[]) · line 2922

Quoted 4 lines
src/evmctl.c +1 -0
@@ -2849,6 +2919,9 @@ int main(int argc, char *argv[])
  		ENGINE_cleanup();
  #endif
  	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000

same comment here

Quoted 5 lines
src/evmctl.c +2 -0
+	CRYPTO_secure_malloc_done();
+#endif
  	ERR_free_strings();
  	EVP_cleanup();
  	BIO_free(NULL);
#2 lore raw

Stefan,

Quoted 34 lines · Stefan Berger
Quoted 23 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

  src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
  1 file changed, 85 insertions(+), 12 deletions(-)

src/evmctl.c static struct option opts[] = { · line 2622

Quoted 7 lines
src/evmctl.c +6 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
  };
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */
+static char *optarg_password(char *optarg)
+{
Quoted 7 lines

Mimi applied my patch that takes the password from an environment variable to the next-testing branch. The code there (imaevm_params.keypass  = getenv("..."))would have to change as well calling this function here. Even though the man page of getenv says that 'The caller must take care not to modify this string, since that would change the environment of the process' we should be able to overwrite the env variable's password value just like here. Maybe for this purpose rename this function to dup_password() ?

/proc/<pid>/environ' is never world readable (unlike cmdline'), so we don't even need to overwrite it there. But, we can.

(Btw, I found the 'EVMCTL_KEY_PASSWORD' patch.)

Quoted 18 lines
Quoted 8 lines
src/evmctl.c +1 -0
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
  	ENGINE *eng = NULL;
  	unsigned long keyid;
  	char *eptr;
+	char *keypass = NULL; /* @secure heap */
  #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
  	OPENSSL_init_crypto(

src/evmctl.c int main(int argc, char *argv[]) · line 2705

Quoted 6 lines
src/evmctl.c +3 -0
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
  #endif
  			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
  #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.

Shouldn't the comparison then not include 0x10100000?

Quoted 1 line

#if OPENSSL_VERSION_NUMBER >= 0x10100000

The number is from include/openssl/opensslv.h' from the time when relevant commit is already applied. So, I use >'. It seems good match for OpenSSL_1_1_0-pre1 too:

  $ git grep OPENSSL_VERSION_NUMBER OpenSSL_1_1_0-pre1:include/openssl/opensslv.h
  OpenSSL_1_1_0-pre1:include/openssl/opensslv.h:# define OPENSSL_VERSION_NUMBER  0x10100001L

Thanks,

Reviewed-by Richard Levitte
#3 lore raw
Quoted 42 lines · Vitaly Chikunov

Stefan,

Quoted 23 lines · Stefan Berger
Quoted 22 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

   src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
   1 file changed, 85 insertions(+), 12 deletions(-)
Quoted 16 lines

src/evmctl.c static struct option opts[] = { · line 2622

Quoted 8 lines
src/evmctl.c +6 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
   };
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */
+static char *optarg_password(char *optarg)
+{

Mimi applied my patch that takes the password from an environment variable to the next-testing branch. The code there (imaevm_params.keypass  = getenv("..."))would have to change as well calling this function here. Even though the man page of getenv says that 'The caller must take care not to modify this string, since that would change the environment of the process' we should be able to overwrite the env variable's password value just like here. Maybe for this purpose rename this function to dup_password() ?

Quoted 2 lines

/proc/<pid>/environ' is never world readable (unlike cmdline'), so we don't even need to overwrite it there. But, we can.

Or you could add an option for whether to overwrite the source...

Quoted 20 lines

(Btw, I found the 'EVMCTL_KEY_PASSWORD' patch.)

Quoted 17 lines
Quoted 8 lines
src/evmctl.c +1 -0
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
   	ENGINE *eng = NULL;
   	unsigned long keyid;
   	char *eptr;
+	char *keypass = NULL; /* @secure heap */
   #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
   	OPENSSL_init_crypto(

src/evmctl.c int main(int argc, char *argv[]) · line 2705

Quoted 6 lines
src/evmctl.c +3 -0
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
   #endif
   			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
   #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.

Shouldn't the comparison then not include 0x10100000?

Quoted 7 lines
Quoted 1 line

#if OPENSSL_VERSION_NUMBER >= 0x10100000

The number is from include/openssl/opensslv.h' from the time when relevant commit is already applied. So, I use >'. It seems good match for OpenSSL_1_1_0-pre1 too:

   $ git grep OPENSSL_VERSION_NUMBER OpenSSL_1_1_0-pre1:include/openssl/opensslv.h
   OpenSSL_1_1_0-pre1:include/openssl/opensslv.h:# define OPENSSL_VERSION_NUMBER  0x10100001L

You are right. I had checkout out this commit here:

commit abd30777cc72029e8a44e4b67201cae8ed3d19c1 (HEAD -> OpenSSL_1_1_0, tag: OpenSSL_1_1_0)
Author: Matt Caswell <[email protected]>
Date:   Thu Aug 25 16:29:18 2016 +0100

    Prepare for 1.1.0 release

It shows this here:

$ grep -r OPENSSL_VERSION_NUM ./include/
./include/openssl/crypto.h:#  define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
./include/openssl/opensslv.h:# define OPENSSL_VERSION_NUMBER 0x1010000fL

There's an 'f' at the 'end'.

  Stefan

Quoted 3 lines

Thanks,

maintainer
#4 lore raw
In reply to Vitaly Chikunov
Quoted 7 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Quoted 6 lines

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

Thanks, it looks good! Just a few questions inline below.

Quoted 56 lines

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

 src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 85 insertions(+), 12 deletions(-)
src/evmctl.c +20 -0
index 5f7c2b8..7bd20f8 100644
@@ -59,6 +59,7 @@
 #include <assert.h>
 
 #include <openssl/asn1.h>
+#include <openssl/crypto.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
@@ -165,6 +166,24 @@ struct tpm_bank_info {
 static char *pcrfile[MAX_PCRFILE];
 static unsigned npcrfile;
 
+#if OPENSSL_VERSION_NUMBER <= 0x10100000
+#warning Your OpenSSL version is too old to have OPENSSL_secure_malloc, \
+	falling back to use plain OPENSSL_malloc.
+#define OPENSSL_secure_malloc	  OPENSSL_malloc
+#define OPENSSL_secure_free	  OPENSSL_free
+/*
+ * Secure heap memory automatically cleared on free, but
+ * OPENSSL_secure_clear_free will be used in case of fallback
+ * to plain OPENSSL_malloc.
+ */
+#define OPENSSL_secure_clear_free OPENSSL_clear_free
+#define OPENSSL_clear_free(ptr, num)		\
+	do {					\
+		OPENSSL_cleanse(ptr, num);	\
+		OPENSSL_free(ptr);		\
+	} while (0)
+#endif
+
 static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
 {
 	FILE *fp;
@@ -1072,6 +1091,7 @@ static int cmd_setxattr_ima(struct command *cmd)
 
 #define MAX_KEY_SIZE 128
 
+/* This function is debugging tool and should not be used for real private data. */
 static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
 {
         const EVP_MD *md;

src/evmctl.c static struct option opts[] = { · line 2622

Quoted 7 lines
src/evmctl.c +4 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
 
 };
 
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */

The reason given for copying the password to secure heap is technically correct, but could we also include the reason that providing the password on the command line is unsafe? Perhaps a modified version of the comment, below, should be included at the beginning of the function comment.

Quoted 77 lines
src/evmctl.c +34 -9
+static char *optarg_password(char *optarg)
+{
+	size_t len;
+	char *keypass;
+
+	if (!optarg)
+		return NULL;
+	len = strlen(optarg);
+	keypass = OPENSSL_secure_malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		log_err("OPENSSL_secure_malloc(%zu) failed\n", len + 1);
+	/*
+	 * This memset does not add real security, just increases
+	 * the chance of password being obscured in ps output.
+	 */
+	memset(optarg, 'X', len);
+	return keypass;
+}
+
+/* Read from TTY into secure heap. */
 static char *get_password(void)
 {
 	struct termios flags, tmp_flags;
 	char *password, *pwd;
-	int passlen = 64;
+	const int passlen = 64;
 
-	password = malloc(passlen);
+	password = OPENSSL_secure_malloc(passlen);
 	if (!password) {
-		perror("malloc");
+		log_err("OPENSSL_secure_malloc(%u) failed\n", passlen);
 		return NULL;
 	}
 
@@ -2614,8 +2660,8 @@ static char *get_password(void)
 	tmp_flags.c_lflag |= ECHONL;
 
 	if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
-		perror("tcsetattr");
-		free(password);
+		log_err("tcsetattr: %s\n", strerror(errno));
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2624,13 +2670,15 @@ static char *get_password(void)
 
 	/* restore terminal */
 	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
-		perror("tcsetattr");
-		free(password);
-		return NULL;
+		log_err("tcsetattr: %s\n", strerror(errno));
+		/*
+		 * Password is already here, so there is no reason
+		 * to stop working on this petty error.
+		 */
 	}
 
 	if (pwd == NULL) {
-		free(password);
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
 	ENGINE *eng = NULL;
 	unsigned long keyid;
 	char *eptr;
+	char *keypass = NULL; /* @secure heap */
 
 #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
 	OPENSSL_init_crypto(

src/evmctl.c int main(int argc, char *argv[]) · line 2709

Quoted 10 lines
src/evmctl.c +7 -0
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
 #endif
 			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
 #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.
+	 * 'Heap size' 8192 is chosen to be big enough, so that any single key
+	 * data could fit. 'Heap minsize' 64 is just to be efficient for small
+	 * buffers.
+	 */

Assuming we aren't guaranteed that the heap size is allocated, should we check (e.g. OPENSSL_secure_actual_size())?

src/evmctl.c int main(int argc, char *argv[]) · line 2711

Quoted 2 lines
src/evmctl.c +2 -0
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");

Either change the test to "!= 1" or also log "and 2 if successful but the heap could not be protected by memory mapping."

src/evmctl.c int main(int argc, char *argv[]) · line 2747

Quoted 12 lines
src/evmctl.c +5 -0
+#endif
+
 	g_argv = argv;
 	g_argc = argc;
 
@@ -2682,10 +2742,18 @@ int main(int argc, char *argv[])
 			imaevm_params.hash_algo = optarg;
 			break;
 		case 'p':
+			if (keypass)
+				OPENSSL_secure_clear_free(keypass,
+							  strlen(keypass));

Is this test needed in case the pasword is provided multiple times? If so, why not skip subsequent passwords?

Quoted 15 lines
src/evmctl.c +7 -2
 			if (optarg)
-				imaevm_params.keypass = optarg;
+				keypass = optarg_password(optarg);
 			else
-				imaevm_params.keypass = get_password();
+				keypass = get_password();
+			if (!keypass) {
+				log_err("Cannot read password\n");
+				goto quit;
+			}
+			imaevm_params.keypass = keypass;
 			break;
 		case 'f':
 			sigfile = 1;

src/evmctl.c int main(int argc, char *argv[]) · line 2912

Quoted 5 lines
src/evmctl.c +1 -1
@@ -2841,7 +2909,9 @@ int main(int argc, char *argv[])
 		if (err < 0)
 			err = 125;
 	}
-
+quit:

Stefan's patch introduced "error:".

src/evmctl.c int main(int argc, char *argv[]) · line 2917

Quoted 5 lines
src/evmctl.c +2 -0
+	if (keypass)
+		OPENSSL_secure_clear_free(keypass, strlen(keypass));
 	if (eng) {
 		ENGINE_finish(eng);
 		ENGINE_free(eng);

and stores the the engine in imaevm_params.

src/evmctl.c int main(int argc, char *argv[]) · line 2927

Quoted 10 lines
src/evmctl.c +3 -0
@@ -2849,6 +2919,9 @@ int main(int argc, char *argv[])
 		ENGINE_cleanup();
 #endif
 	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	CRYPTO_secure_malloc_done();
+#endif
 	ERR_free_strings();
 	EVP_cleanup();
 	BIO_free(NULL);

What was the conclusion in terms of reading the password stored in the environment variable?

thanks,

Mimi

#5 lore raw

Mimi,

Quoted 161 lines · Mimi Zohar
Quoted 7 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Quoted 6 lines

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

Thanks, it looks good! Just a few questions inline below.

Quoted 56 lines

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

 src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 85 insertions(+), 12 deletions(-)
src/evmctl.c +20 -0
index 5f7c2b8..7bd20f8 100644
@@ -59,6 +59,7 @@
 #include <assert.h>
 
 #include <openssl/asn1.h>
+#include <openssl/crypto.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
@@ -165,6 +166,24 @@ struct tpm_bank_info {
 static char *pcrfile[MAX_PCRFILE];
 static unsigned npcrfile;
 
+#if OPENSSL_VERSION_NUMBER <= 0x10100000
+#warning Your OpenSSL version is too old to have OPENSSL_secure_malloc, \
+	falling back to use plain OPENSSL_malloc.
+#define OPENSSL_secure_malloc	  OPENSSL_malloc
+#define OPENSSL_secure_free	  OPENSSL_free
+/*
+ * Secure heap memory automatically cleared on free, but
+ * OPENSSL_secure_clear_free will be used in case of fallback
+ * to plain OPENSSL_malloc.
+ */
+#define OPENSSL_secure_clear_free OPENSSL_clear_free
+#define OPENSSL_clear_free(ptr, num)		\
+	do {					\
+		OPENSSL_cleanse(ptr, num);	\
+		OPENSSL_free(ptr);		\
+	} while (0)
+#endif
+
 static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
 {
 	FILE *fp;
@@ -1072,6 +1091,7 @@ static int cmd_setxattr_ima(struct command *cmd)
 
 #define MAX_KEY_SIZE 128
 
+/* This function is debugging tool and should not be used for real private data. */
 static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
 {
         const EVP_MD *md;

src/evmctl.c static struct option opts[] = { · line 2622

Quoted 7 lines
src/evmctl.c +4 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
 
 };
 
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */

The reason given for copying the password to secure heap is technically correct, but could we also include the reason that providing the password on the command line is unsafe? Perhaps a modified version of the comment, below, should be included at the beginning of the function comment.

Quoted 74 lines
src/evmctl.c +34 -9
+static char *optarg_password(char *optarg)
+{
+	size_t len;
+	char *keypass;
+
+	if (!optarg)
+		return NULL;
+	len = strlen(optarg);
+	keypass = OPENSSL_secure_malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		log_err("OPENSSL_secure_malloc(%zu) failed\n", len + 1);
+	/*
+	 * This memset does not add real security, just increases
+	 * the chance of password being obscured in ps output.
+	 */
+	memset(optarg, 'X', len);
+	return keypass;
+}
+
+/* Read from TTY into secure heap. */
 static char *get_password(void)
 {
 	struct termios flags, tmp_flags;
 	char *password, *pwd;
-	int passlen = 64;
+	const int passlen = 64;
 
-	password = malloc(passlen);
+	password = OPENSSL_secure_malloc(passlen);
 	if (!password) {
-		perror("malloc");
+		log_err("OPENSSL_secure_malloc(%u) failed\n", passlen);
 		return NULL;
 	}
 
@@ -2614,8 +2660,8 @@ static char *get_password(void)
 	tmp_flags.c_lflag |= ECHONL;
 
 	if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
-		perror("tcsetattr");
-		free(password);
+		log_err("tcsetattr: %s\n", strerror(errno));
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2624,13 +2670,15 @@ static char *get_password(void)
 
 	/* restore terminal */
 	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
-		perror("tcsetattr");
-		free(password);
-		return NULL;
+		log_err("tcsetattr: %s\n", strerror(errno));
+		/*
+		 * Password is already here, so there is no reason
+		 * to stop working on this petty error.
+		 */
 	}
 
 	if (pwd == NULL) {
-		free(password);
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
 	ENGINE *eng = NULL;
 	unsigned long keyid;
 	char *eptr;
+	char *keypass = NULL; /* @secure heap */
 
Quoted 16 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2709

Quoted 13 lines
src/evmctl.c +7 -0
 #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
 	OPENSSL_init_crypto(
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
 #endif
 			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
 #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.
+	 * 'Heap size' 8192 is chosen to be big enough, so that any single key
+	 * data could fit. 'Heap minsize' 64 is just to be efficient for small
+	 * buffers.
+	 */

Assuming we aren't guaranteed that the heap size is allocated, should we check (e.g. OPENSSL_secure_actual_size())?

We guaranteed that secure heap is allocated by return value of CRYPTO_secure_malloc_init.

OPENSSL_secure_actual_size tell if OPENSSL_secure_malloc is actually provided bigger memory fragment than we requested (to reduce fragmentation). Why care about it?

Quoted 6 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2711

Quoted 3 lines
src/evmctl.c +2 -0
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");

Either change the test to "!= 1" or also log "and 2 if successful but the heap could not be protected by memory mapping."

Why should we care about implementation details of successful allocation? If they don't think it's secure heap they should not return success. And they say about its return value "and 2 if successful but", so it's successful.

Quoted 6 lines
Quoted 6 lines
src/evmctl.c +2 -0
+#endif
+
 	g_argv = argv;
 	g_argc = argc;
 
Quoted 9 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2747

Quoted 7 lines
src/evmctl.c +3 -0
@@ -2682,10 +2742,18 @@ int main(int argc, char *argv[])
 			imaevm_params.hash_algo = optarg;
 			break;
 		case 'p':
+			if (keypass)
+				OPENSSL_secure_clear_free(keypass,
+							  strlen(keypass));

Is this test needed in case the pasword is provided multiple times?

It does not care, it just frees memory so there is no leaks. (Perhaps. it will be cleaned on CRYPTO_secure_malloc_done anyway). I can remove this check, but why. Then we don't need to free() most of allocations, because exit will care about all memory. But we free them anyway.

Quoted 1 line

If so, why not skip subsequent passwords?

Old code favored last specified password and I'm just adding secure heap to it. I don't see that preference to first of last password is better for UX.

Quoted 24 lines
Quoted 16 lines
src/evmctl.c +7 -2
 			if (optarg)
-				imaevm_params.keypass = optarg;
+				keypass = optarg_password(optarg);
 			else
-				imaevm_params.keypass = get_password();
+				keypass = get_password();
+			if (!keypass) {
+				log_err("Cannot read password\n");
+				goto quit;
+			}
+			imaevm_params.keypass = keypass;
 			break;
 		case 'f':
 			sigfile = 1;

src/evmctl.c int main(int argc, char *argv[]) · line 2912

Quoted 5 lines
src/evmctl.c +1 -1
@@ -2841,7 +2909,9 @@ int main(int argc, char *argv[])
 		if (err < 0)
 			err = 125;
 	}
-
+quit:

Stefan's patch introduced "error:".

Quoted 7 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2917

Quoted 5 lines
src/evmctl.c +2 -0
+	if (keypass)
+		OPENSSL_secure_clear_free(keypass, strlen(keypass));
 	if (eng) {
 		ENGINE_finish(eng);
 		ENGINE_free(eng);

and stores the the engine in imaevm_params.

OK I will wait when his patches will appear in git, so we do not collide with each other.

Quoted 14 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2927

Quoted 11 lines
src/evmctl.c +3 -0
@@ -2849,6 +2919,9 @@ int main(int argc, char *argv[])
 		ENGINE_cleanup();
 #endif
 	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	CRYPTO_secure_malloc_done();
+#endif
 	ERR_free_strings();
 	EVP_cleanup();
 	BIO_free(NULL);

What was the conclusion in terms of reading the password stored in the environment variable?

I will add handling for it similar to password from optarg.

Thanks,

Quoted 4 lines

thanks,

Mimi

maintainer
#6 lore raw

Hi Vitaly,

Quoted 181 lines · Vitaly Chikunov

Mimi,

Quoted 161 lines · Mimi Zohar
Quoted 7 lines · Vitaly Chikunov

After CRYPTO_secure_malloc_init OpenSSL will automatically store private keys in secure heap. OPENSSL_secure_malloc(3):

If a secure heap is used, then private key BIGNUM values are stored there. This protects long-term storage of private keys, but will not necessarily put all intermediate values and computations there.

Quoted 6 lines

Additionally, we try to keep user passwords in secure heap too. This facility is only available since OpenSSL_1_1_0-pre1.

Thanks, it looks good! Just a few questions inline below.

Quoted 56 lines

---
Change since v3:
- Undo secure heap handling from (file2bin and) calc_evm_hmac since this is debugging tool only. Add comment about this.
- Since there is only code removals and new comments I keep Reviewed-by tag.

 src/evmctl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 85 insertions(+), 12 deletions(-)
src/evmctl.c +20 -0
index 5f7c2b8..7bd20f8 100644
@@ -59,6 +59,7 @@
 #include <assert.h>
 
 #include <openssl/asn1.h>
+#include <openssl/crypto.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
 #include <openssl/hmac.h>
@@ -165,6 +166,24 @@ struct tpm_bank_info {
 static char *pcrfile[MAX_PCRFILE];
 static unsigned npcrfile;
 
+#if OPENSSL_VERSION_NUMBER <= 0x10100000
+#warning Your OpenSSL version is too old to have OPENSSL_secure_malloc, \
+	falling back to use plain OPENSSL_malloc.
+#define OPENSSL_secure_malloc	  OPENSSL_malloc
+#define OPENSSL_secure_free	  OPENSSL_free
+/*
+ * Secure heap memory automatically cleared on free, but
+ * OPENSSL_secure_clear_free will be used in case of fallback
+ * to plain OPENSSL_malloc.
+ */
+#define OPENSSL_secure_clear_free OPENSSL_clear_free
+#define OPENSSL_clear_free(ptr, num)		\
+	do {					\
+		OPENSSL_cleanse(ptr, num);	\
+		OPENSSL_free(ptr);		\
+	} while (0)
+#endif
+
 static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
 {
 	FILE *fp;
@@ -1072,6 +1091,7 @@ static int cmd_setxattr_ima(struct command *cmd)
 
 #define MAX_KEY_SIZE 128
 
+/* This function is debugging tool and should not be used for real private data. */
 static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
 {
         const EVP_MD *md;

src/evmctl.c static struct option opts[] = { · line 2622

Quoted 7 lines
src/evmctl.c +4 -0
@@ -2596,15 +2616,41 @@ static struct option opts[] = {
 
 };
 
+/*
+ * Copy password from optarg into secure heap, so it could be
+ * freed in the same way as a result of get_password().
+ */

The reason given for copying the password to secure heap is technically correct, but could we also include the reason that providing the password on the command line is unsafe? Perhaps a modified version of the comment, below, should be included at the beginning of the function comment.

Quoted 74 lines
src/evmctl.c +34 -9
+static char *optarg_password(char *optarg)
+{
+	size_t len;
+	char *keypass;
+
+	if (!optarg)
+		return NULL;
+	len = strlen(optarg);
+	keypass = OPENSSL_secure_malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		log_err("OPENSSL_secure_malloc(%zu) failed\n", len + 1);
+	/*
+	 * This memset does not add real security, just increases
+	 * the chance of password being obscured in ps output.
+	 */
+	memset(optarg, 'X', len);
+	return keypass;
+}
+
+/* Read from TTY into secure heap. */
 static char *get_password(void)
 {
 	struct termios flags, tmp_flags;
 	char *password, *pwd;
-	int passlen = 64;
+	const int passlen = 64;
 
-	password = malloc(passlen);
+	password = OPENSSL_secure_malloc(passlen);
 	if (!password) {
-		perror("malloc");
+		log_err("OPENSSL_secure_malloc(%u) failed\n", passlen);
 		return NULL;
 	}
 
@@ -2614,8 +2660,8 @@ static char *get_password(void)
 	tmp_flags.c_lflag |= ECHONL;
 
 	if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
-		perror("tcsetattr");
-		free(password);
+		log_err("tcsetattr: %s\n", strerror(errno));
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2624,13 +2670,15 @@ static char *get_password(void)
 
 	/* restore terminal */
 	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
-		perror("tcsetattr");
-		free(password);
-		return NULL;
+		log_err("tcsetattr: %s\n", strerror(errno));
+		/*
+		 * Password is already here, so there is no reason
+		 * to stop working on this petty error.
+		 */
 	}
 
 	if (pwd == NULL) {
-		free(password);
+		OPENSSL_secure_free(password);
 		return NULL;
 	}
 
@@ -2643,6 +2691,7 @@ int main(int argc, char *argv[])
 	ENGINE *eng = NULL;
 	unsigned long keyid;
 	char *eptr;
+	char *keypass = NULL; /* @secure heap */
 
Quoted 16 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2709

Quoted 13 lines
src/evmctl.c +7 -0
 #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
 	OPENSSL_init_crypto(
@@ -2651,6 +2700,17 @@ int main(int argc, char *argv[])
 #endif
 			    OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
 #endif
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * This facility is available since OpenSSL_1_1_0-pre1.
+	 * 'Heap size' 8192 is chosen to be big enough, so that any single key
+	 * data could fit. 'Heap minsize' 64 is just to be efficient for small
+	 * buffers.
+	 */

Assuming we aren't guaranteed that the heap size is allocated, should we check (e.g. OPENSSL_secure_actual_size())?

Quoted 6 lines

We guaranteed that secure heap is allocated by return value of CRYPTO_secure_malloc_init.

OPENSSL_secure_actual_size tell if OPENSSL_secure_malloc is actually provided bigger memory fragment than we requested (to reduce fragmentation). Why care about it?

We wouldn't care, assuming we're guaranteed the heap size requested was allocated and not the minimum heap size. Hm, wondering what would be the purpose of the minimum heap size ...

Quoted 8 lines
Quoted 7 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2711

Quoted 4 lines
src/evmctl.c +2 -0
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");

Either change the test to "!= 1" or also log "and 2 if successful but the heap could not be protected by memory mapping."

Quoted 4 lines

Why should we care about implementation details of successful allocation? If they don't think it's secure heap they should not return success. And they say about its return value "and 2 if successful but", so it's successful.

If you're correct, then there is no reason ever for returning 2. Without documentation or digging into OpenSSL, this makes no sense to me. Perhaps "secure heap" has some kernel dependency, requires HW, or something else entirely. Not that there is much we could or would do, but simply ignoring it just seems wrong.

Quoted 23 lines
Quoted 8 lines
Quoted 8 lines
src/evmctl.c +2 -0
+#endif
+
 	g_argv = argv;
 	g_argc = argc;
 
Quoted 9 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2747

Quoted 7 lines
src/evmctl.c +3 -0
@@ -2682,10 +2742,18 @@ int main(int argc, char *argv[])
 			imaevm_params.hash_algo = optarg;
 			break;
 		case 'p':
+			if (keypass)
+				OPENSSL_secure_clear_free(keypass,
+							  strlen(keypass));

Is this test needed in case the pasword is provided multiple times?

It does not care, it just frees memory so there is no leaks. (Perhaps. it will be cleaned on CRYPTO_secure_malloc_done anyway). I can remove this check, but why. Then we don't need to free() most of allocations, because exit will care about all memory. But we free them anyway.

Quoted 5 lines
Quoted 1 line

If so, why not skip subsequent passwords?

Old code favored last specified password and I'm just adding secure heap to it. I don't see that preference to first of last password is better for UX.

Ok

Quoted 50 lines
Quoted 24 lines
Quoted 16 lines
src/evmctl.c +7 -2
 			if (optarg)
-				imaevm_params.keypass = optarg;
+				keypass = optarg_password(optarg);
 			else
-				imaevm_params.keypass = get_password();
+				keypass = get_password();
+			if (!keypass) {
+				log_err("Cannot read password\n");
+				goto quit;
+			}
+			imaevm_params.keypass = keypass;
 			break;
 		case 'f':
 			sigfile = 1;

src/evmctl.c int main(int argc, char *argv[]) · line 2912

Quoted 5 lines
src/evmctl.c +1 -1
@@ -2841,7 +2909,9 @@ int main(int argc, char *argv[])
 		if (err < 0)
 			err = 125;
 	}
-
+quit:

Stefan's patch introduced "error:".

Quoted 7 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2917

Quoted 5 lines
src/evmctl.c +2 -0
+	if (keypass)
+		OPENSSL_secure_clear_free(keypass, strlen(keypass));
 	if (eng) {
 		ENGINE_finish(eng);
 		ENGINE_free(eng);

and stores the the engine in imaevm_params.

OK I will wait when his patches will appear in git, so we do not collide with each other.

Quoted 14 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2927

Quoted 11 lines
src/evmctl.c +3 -0
@@ -2849,6 +2919,9 @@ int main(int argc, char *argv[])
 		ENGINE_cleanup();
 #endif
 	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	CRYPTO_secure_malloc_done();
+#endif
 	ERR_free_strings();
 	EVP_cleanup();
 	BIO_free(NULL);

What was the conclusion in terms of reading the password stored in the environment variable?

Quoted 1 line

I will add handling for it similar to password from optarg.

thanks,

Mimi

#7 lore raw

Mimi,

Quoted 6 lines · Mimi Zohar
Quoted 6 lines
Quoted 2 lines

Assuming we aren't guaranteed that the heap size is allocated, should we check (e.g. OPENSSL_secure_actual_size())?

We guaranteed that secure heap is allocated by return value of CRYPTO_secure_malloc_init.

Quoted 7 lines
Quoted 3 lines

OPENSSL_secure_actual_size tell if OPENSSL_secure_malloc is actually provided bigger memory fragment than we requested (to reduce fragmentation). Why care about it?

We wouldn't care, assuming we're guaranteed the heap size requested was allocated and not the minimum heap size. Hm, wondering what would be the purpose of the minimum heap size ...

Perhaps, just to control heap fragmentation.

Quoted 11 lines
Quoted 10 lines
Quoted 5 lines

src/evmctl.c int main(int argc, char *argv[]) · line 2711

Quoted 2 lines
src/evmctl.c +2 -0
+	if (!CRYPTO_secure_malloc_init(8192, 64))
+		log_err("CRYPTO_secure_malloc_init() failed\n");

Either change the test to "!= 1" or also log "and 2 if successful but the heap could not be protected by memory mapping."

Why should we care about implementation details of successful allocation? If they don't think it's secure heap they should not return success. And they say about its return value "and 2 if successful but", so it's successful.

Quoted 5 lines

If you're correct, then there is no reason ever for returning 2. Without documentation or digging into OpenSSL, this makes no sense to me. Perhaps "secure heap" has some kernel dependency, requires HW, or something else entirely. Not that there is much we could or would do, but simply ignoring it just seems wrong.

I looked at CRYPTO_secure_malloc_init and it seems they return 2 if mlock/madvise is failed. I think it's major enough to report it, so I will add it.

Thanks,

maintainer
#8 lore raw
In reply to Mimi Zohar

Hi Vitaly, Stefan,

Quoted 2 lines · Mimi Zohar

What was the conclusion in terms of reading the password stored in the environment variable?

If this is an issue, perhaps call the equivalent of optarg_password() to store the environment variable in secure heap memory.

Something like:
imaevm_params.keypass =
optarg_password(getenv("EVMCTL_KEY_PASSWORD"));

optarg_password() would then become a wrapper around the new function.

thanks,

Mimi