[PATCH ima-evm-utils] Improve memory handling for private keys and passwords

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



After CRYPTO_secure_malloc_init OpenSSL will store private keys in
secure heap. This facility is only available since OpenSSL_1_1_0-pre1
and enabled for 'ima_sign', 'sign', 'sign_hash', and 'hmac'.

setvbuf(3) _IONBF is used to hopefully avoid private key and password
being stored inside of stdio buffers.

Unfortunately, secure heap is not used for the passwords (`-p') due to
absence of its support in the older OpenSSL where ima-evem-utils still
should work, thus simple cleansing of password memory is used where
possible to shorten its lifespan.

Signed-off-by: Vitaly Chikunov <vt@xxxxxxxxxxxx>
---
Perhaps, it will conflict with Bruno's patch, we should decide which
one goes first if this will be accepted.

 src/evmctl.c    | 83 ++++++++++++++++++++++++++++++++++++++++++++++---
 src/libimaevm.c | 14 ++++++++-
 2 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/src/evmctl.c b/src/evmctl.c
index a8065bb..3275464 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -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,23 @@ struct tpm_bank_info {
 static char *pcrfile[MAX_PCRFILE];
 static unsigned npcrfile;
 
+static void init_openssl_secure_memory(void)
+{
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	/*
+	 * man CRYPTO_secure_malloc_init for explanation of the values.
+	 * 8192 is chosen to be big enough, so that any single key data could fit.
+	 */
+# define SECURE_HEAP_SIZE 8192
+# define SECURE_HEAP_MINSIZE 64
+	/*
+	 * Enable secure storage when working with private keys.
+	 * This facility is available since OpenSSL_1_1_0-pre1.
+	 */
+	CRYPTO_secure_malloc_init(SECURE_HEAP_SIZE, SECURE_HEAP_MINSIZE);
+#endif
+}
+
 static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
 {
 	FILE *fp;
@@ -221,6 +239,8 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size)
 		fclose(fp);
 		return NULL;
 	}
+	/* Disable buffering because it's used to read private HMAC key too. */
+	setvbuf(fp, NULL, _IONBF, 0);
 	if (fread(data, len, 1, fp) != 1) {
 		log_err("Failed to fread %zu bytes: %s\n", len, name);
 		fclose(fp);
@@ -723,6 +743,7 @@ static int sign_ima_file(const char *file)
 
 static int cmd_sign_ima(struct command *cmd)
 {
+	init_openssl_secure_memory();
 	return do_cmd(cmd, sign_ima_file);
 }
 
@@ -737,6 +758,7 @@ static int cmd_sign_hash(struct command *cmd)
 	unsigned char sig[MAX_SIGNATURE_SIZE] = "\x03";
 	int siglen;
 
+	init_openssl_secure_memory();
 	key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
 
 	/* support reading hash (eg. output of shasum) */
@@ -796,6 +818,7 @@ static int sign_evm_path(const char *file)
 
 static int cmd_sign_evm(struct command *cmd)
 {
+	init_openssl_secure_memory();
 	return do_cmd(cmd, sign_evm_path);
 }
 
@@ -1104,6 +1127,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
 
 	if (keylen > sizeof(evmkey)) {
 		log_err("key is too long: %d\n", keylen);
+		OPENSSL_cleanse(key, keylen);
 		goto out;
 	}
 
@@ -1111,6 +1135,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
 	memcpy(evmkey, key, keylen);
 	if (keylen < sizeof(evmkey))
 		memset(evmkey + keylen, 0, sizeof(evmkey) - keylen);
+	OPENSSL_cleanse(key, keylen);
 
 	if (lstat(file, &st)) {
 		log_err("Failed to stat: %s\n", file);
@@ -1148,6 +1173,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
 	}
 
 	err = !HMAC_Init_ex(pctx, evmkey, sizeof(evmkey), md, NULL);
+	OPENSSL_cleanse(evmkey, sizeof(evmkey));
 	if (err) {
 		log_err("HMAC_Init() failed\n");
 		goto out;
@@ -1259,6 +1285,7 @@ static int cmd_hmac_evm(struct command *cmd)
 	const char *key, *file = g_argv[optind++];
 	int err;
 
+	init_openssl_secure_memory();
 	if (!file) {
 		log_err("Parameters missing\n");
 		print_usage(cmd);
@@ -2592,6 +2619,32 @@ static struct option opts[] = {
 
 };
 
+/*
+ * Copy password from optarg into malloc'd memory, 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 = malloc(len + 1);
+	if (keypass)
+		memcpy(keypass, optarg, len + 1);
+	else
+		perror("malloc");
+	/*
+	 * 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 password from TTY. */
 static char *get_password(void)
 {
 	struct termios flags, tmp_flags;
@@ -2614,6 +2667,7 @@ static char *get_password(void)
 		free(password);
 		return NULL;
 	}
+	setvbuf(stdin, NULL, _IONBF, 0);
 
 	printf("PEM password: ");
 	pwd = fgets(password, passlen, stdin);
@@ -2621,8 +2675,10 @@ static char *get_password(void)
 	/* restore terminal */
 	if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
 		perror("tcsetattr");
-		free(password);
-		return NULL;
+		/*
+		 * Password is already here, so there is no point
+		 * to stop working on this petty error.
+		 */
 	}
 
 	return pwd;
@@ -2634,6 +2690,7 @@ int main(int argc, char *argv[])
 	ENGINE *eng = NULL;
 	unsigned long keyid;
 	char *eptr;
+	char *keypass = NULL;
 
 #if !(OPENSSL_VERSION_NUMBER < 0x10100000)
 	OPENSSL_init_crypto(
@@ -2673,10 +2730,19 @@ int main(int argc, char *argv[])
 			imaevm_params.hash_algo = optarg;
 			break;
 		case 'p':
+			if (keypass) {
+				OPENSSL_cleanse(keypass, strlen(keypass));
+				free(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;
@@ -2833,6 +2899,15 @@ int main(int argc, char *argv[])
 			err = 125;
 	}
 
+quit:
+	if (keypass) {
+		OPENSSL_cleanse(keypass, strlen(keypass));
+		free(keypass);
+	}
+#if OPENSSL_VERSION_NUMBER > 0x10100000
+	CRYPTO_secure_malloc_done();
+#endif
+
 	if (eng) {
 		ENGINE_finish(eng);
 		ENGINE_free(eng);
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 19f1041..7bf3ba9 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -726,6 +726,10 @@ static int read_keyid_from_cert(uint32_t *keyid_be, const char *certfile, int tr
 		log_err("Cannot open %s: %s\n", certfile, strerror(errno));
 		return -1;
 	}
+
+	/* There could be private key, thus disable buffering. */
+	setvbuf(fp, NULL, _IONBF, 0);
+
 	if (!PEM_read_X509(fp, &x, NULL, NULL)) {
 		if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
 			ERR_clear_error();
@@ -799,6 +803,7 @@ static EVP_PKEY *read_priv_pkey(const char *keyfile, const char *keypass)
 		log_err("Failed to open keyfile: %s\n", keyfile);
 		return NULL;
 	}
+	setvbuf(fp, NULL, _IONBF, 0);
 	pkey = PEM_read_PrivateKey(fp, NULL, NULL, (void *)keypass);
 	if (!pkey) {
 		log_err("Failed to PEM_read_PrivateKey key file: %s\n",
@@ -1021,8 +1026,15 @@ err:
 
 int sign_hash(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, const char *keypass, unsigned char *sig)
 {
-	if (keypass)
+	if (keypass && keypass != imaevm_params.keypass) {
+		/*
+		 * Cannot cleanse previous imaevm_params.keypass value, due to
+		 * it's being const, but can warn user.
+		 */
+		if (imaevm_params.keypass)
+			log_err("Overwriting non-empty imaevm_params.keypass\n");
 		imaevm_params.keypass = keypass;
+	}
 
 	return imaevm_params.x509 ?
 		sign_hash_v2(hashalgo, hash, size, keyfile, sig) :
-- 
2.29.3




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux