From: Dan Aloni <[email protected]>
Hi All,
There has been a lot of progress in recent times regarding the removal
of sensitive information from dmesg (pointers, etc.), so I figured - why
not encrypt it all? However, I have not found any existing discussions
or references regarding this technical direction.
I am not sure that desktop and power users would like to have their
kernel message encrypted, but there are scenarios such as in mobile
devices, where only the developers, makers of devices, may actually
benefit from access to kernel prints messages, and the users may be
more protected from exploits.
In its present form, it is an RFC, so that before investing more time
developing it further, I would receive comments regarding its viability
and suggestions for design.
Dan Aloni (5):
crypto: fix memory leak in rsa-kcs1pad encryption
certs: allow in-kernel access of trusted keys
kernel/printk: allow kmsg to be encrypted using public key encryption
tools: add dmesg decryption program
docs: add dmesg encryption doc
Documentation/admin-guide/dmesg-encryption.rst | 77 +++++
certs/system_keyring.c | 56 +++-
crypto/rsa-pkcs1pad.c | 9 -
include/keys/system_keyring.h | 3 +
include/uapi/linux/kmsg.h | 18 ++
init/Kconfig | 10 +
kernel/printk/printk.c | 422 +++++++++++++++++++++++++
tools/Makefile | 5 +-
tools/kmsg/.gitignore | 1 +
tools/kmsg/Makefile | 14 +
tools/kmsg/dmesg-decipher.c | 316 ++++++++++++++++++
11 files changed, 920 insertions(+), 11 deletions(-)
create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
create mode 100644 include/uapi/linux/kmsg.h
create mode 100644 tools/kmsg/.gitignore
create mode 100644 tools/kmsg/Makefile
create mode 100644 tools/kmsg/dmesg-decipher.c
--
2.13.6
From: Dan Aloni <[email protected]>
The encryption mode of pkcs1pad never uses out_sg and out_buf, so
there's no need to allocate the buffer, which presently is not even
being freed.
CC: Herbert Xu <[email protected]>
Signed-off-by: Dan Aloni <[email protected]>
---
crypto/rsa-pkcs1pad.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
index 2908f93c3e55..e8354084ef4e 100644
--- a/crypto/rsa-pkcs1pad.c
+++ b/crypto/rsa-pkcs1pad.c
@@ -261,15 +261,6 @@ static int pkcs1pad_encrypt(struct akcipher_request *req)
pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf,
ctx->key_size - 1 - req->src_len, req->src);
- req_ctx->out_buf = kmalloc(ctx->key_size, GFP_KERNEL);
- if (!req_ctx->out_buf) {
- kfree(req_ctx->in_buf);
- return -ENOMEM;
- }
-
- pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
- ctx->key_size, NULL);
-
akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
pkcs1pad_encrypt_sign_complete_cb, req);
--
2.13.6
From: Dan Aloni <[email protected]>
This commit enables the kernel to encrypt the free-form text that
is generated by printk() before it is brought up to `dmesg` in
userspace.
The encryption is made using one of the trusted public keys which
are kept built-in inside the kernel. These keys are presently
also used for verifying kernel modules and userspace-supplied
firmwares.
Signed-off-by: Dan Aloni <[email protected]>
---
include/uapi/linux/kmsg.h | 18 ++
init/Kconfig | 10 ++
kernel/printk/printk.c | 422 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 450 insertions(+)
create mode 100644 include/uapi/linux/kmsg.h
diff --git a/include/uapi/linux/kmsg.h b/include/uapi/linux/kmsg.h
new file mode 100644
index 000000000000..ae74f026d727
--- /dev/null
+++ b/include/uapi/linux/kmsg.h
@@ -0,0 +1,18 @@
+#ifndef _LINUX_UAPI_KMSG_H
+#define _LINUX_UAPI_KMSG_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct kmsg_ioctl_get_encrypted_key {
+ void __user *output_buffer;
+ __u64 buffer_size;
+ __u64 key_size;
+};
+
+#define KMSG_IOCTL_BASE 0x42
+
+#define KMSG_IOCTL__GET_ENCRYPTED_KEY _IOWR(KMSG_IOCTL_BASE, 0xe1, \
+ struct kmsg_ioctl_get_encrypted_key)
+
+#endif /* _LINUX_DN_H */
diff --git a/init/Kconfig b/init/Kconfig
index 2934249fba46..3990fe24a9d3 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1757,6 +1757,16 @@ config MODULE_SIG
debuginfo strip done by some packagers (such as rpmbuild) and
inclusion into an initramfs that wants the module size reduced.
+config KMSG_ENCRYPTION
+ bool "Encrypt /dev/kmsg (viewing dmesg will require decryption!)"
+ depends on SYSTEM_TRUSTED_KEYRING
+ help
+ This enables strong encryption of messages generated by the kernel,
+ to defend against most kinds of information leaks.
+
+ Note that this option adds the OpenSSL development packages as a
+ kernel build dependency so that certificates can be generated.
+
config MODULE_SIG_FORCE
bool "Require modules to be validly signed"
depends on MODULE_SIG
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index b9006617710f..c50b9cb60b82 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -48,6 +48,13 @@
#include <linux/sched/clock.h>
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
+#include <linux/scatterlist.h>
+#include <linux/random.h>
+#include <crypto/akcipher.h>
+#include <crypto/public_key.h>
+#include <crypto/aead.h>
+#include <uapi/linux/kmsg.h>
+#include <keys/system_keyring.h>
#include <linux/uaccess.h>
#include <asm/sections.h>
@@ -100,6 +107,10 @@ enum devkmsg_log_masks {
DEVKMSG_LOG_MASK_LOCK = BIT(__DEVKMSG_LOG_BIT_LOCK),
};
+#define CRYPT_KMSG_KEY_LEN 16
+#define CRYPT_KMSG_AUTH_LEN 16
+#define CRYPT_KMSG_TEXT_META_MAX 32
+
/* Keep both the 'on' and 'off' bits clear, i.e. ratelimit by default: */
#define DEVKMSG_LOG_MASK_DEFAULT 0
@@ -744,12 +755,33 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
return p - buf;
}
+#ifdef CONFIG_KMSG_ENCRYPTION
+static int __ro_after_init kmsg_encrypt = 1;
+static int __init control_kmsg_encrypt(char *str)
+{
+ get_option(&str, &kmsg_encrypt);
+ return 0;
+}
+__setup("kmsg.encrypt=", control_kmsg_encrypt);
+
+struct devkmsg_crypt {
+ u8 key[CRYPT_KMSG_KEY_LEN];
+ u8 *encrypted_key;
+ size_t encrypted_key_len;
+ bool encrypted_key_read;
+ struct crypto_aead *sk_tfm;
+};
+#else
+struct devkmsg_crypt {};
+#endif
+
/* /dev/kmsg - userspace message inject/listen interface */
struct devkmsg_user {
u64 seq;
u32 idx;
struct ratelimit_state rs;
struct mutex lock;
+ struct devkmsg_crypt crypt;
char buf[CONSOLE_EXT_LOG_MAX];
};
@@ -816,6 +848,330 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from)
return ret;
}
+#ifdef CONFIG_KMSG_ENCRYPTION
+
+static int devkmsg_encrypt_key(struct devkmsg_crypt *crypt,
+ struct crypto_akcipher *ak_tfm)
+{
+ const struct public_key *pkey;
+ struct akcipher_request *req;
+ unsigned int out_len_max;
+ struct scatterlist src, dst;
+ void *outbuf_enc = NULL;
+ struct crypto_wait wait;
+ struct key *key;
+ int err;
+
+ if (!kmsg_encrypt)
+ return 0;
+
+ key = find_trusted_asymmetric_key(NULL, NULL);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ pkey = key->payload.data[asym_crypto];
+ BUG_ON(!pkey);
+
+ err = -ENOMEM;
+ req = akcipher_request_alloc(ak_tfm, GFP_KERNEL);
+ if (!req)
+ goto exit2;
+
+ err = crypto_akcipher_set_pub_key(ak_tfm,
+ pkey->key, pkey->keylen);
+ if (err)
+ goto exit;
+
+ err = -ENOMEM;
+ out_len_max = crypto_akcipher_maxsize(ak_tfm);
+ outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
+ if (!outbuf_enc)
+ goto exit;
+
+ crypto_init_wait(&wait);
+ sg_init_one(&src, crypt->key, sizeof(crypt->key));
+ sg_init_one(&dst, outbuf_enc, out_len_max);
+ akcipher_request_set_crypt(req, &src, &dst, sizeof(crypt->key),
+ out_len_max);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ err = crypto_wait_req(crypto_akcipher_encrypt(req), &wait);
+ if (err) {
+ kfree(outbuf_enc);
+ goto exit;
+ }
+
+ crypt->encrypted_key_len = req->dst_len;
+ crypt->encrypted_key = outbuf_enc;
+
+exit:
+ akcipher_request_free(req);
+exit2:
+ key_put(key);
+ return err;
+}
+
+static int devkmsg_crypt_init(struct devkmsg_crypt *crypt)
+{
+ struct crypto_akcipher *ak_tfm;
+ struct crypto_aead *sk_tfm;
+ int err;
+
+ if (!kmsg_encrypt)
+ return 0;
+
+ crypt->encrypted_key = NULL;
+ crypt->encrypted_key_len = 0;
+ crypt->encrypted_key_read = false;
+
+ sk_tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
+ if (IS_ERR(sk_tfm))
+ return PTR_ERR(sk_tfm);
+
+ get_random_bytes(crypt->key, sizeof(crypt->key));
+
+ err = crypto_aead_setkey(sk_tfm, crypt->key, sizeof(crypt->key));
+ if (err < 0)
+ goto fail;
+
+ err = crypto_aead_setauthsize(sk_tfm, CRYPT_KMSG_AUTH_LEN);
+ if (err < 0)
+ goto fail;
+
+ ak_tfm = crypto_alloc_akcipher("pkcs1pad(rsa,sha256)", 0, 0);
+ if (IS_ERR(ak_tfm)) {
+ err = PTR_ERR(ak_tfm);
+ goto fail;
+ }
+
+ err = devkmsg_encrypt_key(crypt, ak_tfm);
+ crypto_free_akcipher(ak_tfm);
+
+ if (err < 0)
+ goto fail;
+
+ crypt->sk_tfm = sk_tfm;
+ return 0;
+
+fail:
+ crypto_free_aead(sk_tfm);
+ return err;
+}
+
+static void devkmsg_crypt_free(struct devkmsg_crypt *crypt)
+{
+ if (!kmsg_encrypt)
+ return;
+
+ crypto_free_aead(crypt->sk_tfm);
+ kfree(crypt->encrypted_key);
+}
+
+static const char hex_legend[] = "0123456789abcdef";
+
+static int devkmsg_encrypt_inplace(struct devkmsg_user *user,
+ size_t hdr_len, size_t len,
+ size_t *out_len)
+{
+ DECLARE_CRYPTO_WAIT(wait);
+ const char *newline_pos;
+ const char *prefix = "M:";
+ char suffix[CRYPT_KMSG_TEXT_META_MAX];
+ int all_cryptmsg_encoded_len;
+ int ciphertext_with_auth;
+ int dict_len;
+ int i, j;
+ int prefix_len = strlen(prefix);
+ int suffix_size;
+ size_t len_no_newline;
+ ssize_t ret;
+ struct aead_request *aead_req;
+ struct scatterlist sgio;
+ u8 *iv;
+ unsigned int iv_len;
+
+ if (!kmsg_encrypt)
+ return 0;
+
+ newline_pos = strnchr(user->buf, len, '\n');
+ if (!newline_pos)
+ return -EINVAL;
+
+ /* We do not encrypt the dict, but only the free-form text. */
+ len_no_newline = newline_pos - user->buf;
+
+ /* If dict_len == 1 it's an empty dict, only a '\n' */
+ dict_len = len - len_no_newline;
+
+ aead_req = aead_request_alloc(user->crypt.sk_tfm, GFP_KERNEL);
+ if (!aead_req)
+ return -ENOMEM;
+
+ iv_len = crypto_aead_ivsize(user->crypt.sk_tfm);
+ ciphertext_with_auth = (len_no_newline - hdr_len
+ + CRYPT_KMSG_AUTH_LEN + iv_len) * 2;
+ all_cryptmsg_encoded_len = hdr_len + prefix_len + ciphertext_with_auth;
+
+ suffix_size =
+ scnprintf(suffix, sizeof(suffix), ",%u,%u",
+ CRYPT_KMSG_AUTH_LEN, iv_len);
+
+ /*
+ * Check that we are not overflowing with the rearrangement
+ * of the encrypted message.
+ */
+ ret = -EINVAL;
+ if (all_cryptmsg_encoded_len + suffix_size + dict_len +
+ CRYPT_KMSG_TEXT_META_MAX > sizeof(user->buf))
+ goto out;
+
+ /* Move away the dict farther down so we don't overwrite it */
+ if (dict_len > 0)
+ memmove(&user->buf[all_cryptmsg_encoded_len + suffix_size],
+ &user->buf[len_no_newline],
+ dict_len);
+
+ /* Initialize IV */
+ iv = &user->buf[len_no_newline + CRYPT_KMSG_AUTH_LEN];
+
+ get_random_bytes(iv, iv_len);
+
+ /* Do the encryption */
+
+ sg_init_one(&sgio, user->buf + hdr_len,
+ len_no_newline + CRYPT_KMSG_AUTH_LEN - hdr_len);
+
+ aead_request_set_crypt(aead_req, &sgio, &sgio,
+ len_no_newline - hdr_len, iv);
+ aead_request_set_ad(aead_req, 0);
+ aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_aead_encrypt(aead_req), &wait);
+
+ if (ret)
+ goto out;
+
+ /* Hex-encode the ciphertext + auth code + IV */
+
+ BUG_ON(hdr_len <= 1);
+
+ for (j = len_no_newline - 1 + CRYPT_KMSG_AUTH_LEN + iv_len,
+ i = all_cryptmsg_encoded_len - 2;
+ i >= hdr_len + prefix_len; j--, i -= 2)
+ {
+ u8 octet = user->buf[j];
+ user->buf[i + 0] = hex_legend[(octet >> 4) & 0xf];
+ user->buf[i + 1] = hex_legend[(octet >> 0) & 0xf];
+ }
+
+ /* Add prefixes and suffixes */
+
+ memcpy(&user->buf[hdr_len], prefix, prefix_len);
+ memcpy(&user->buf[all_cryptmsg_encoded_len], suffix, suffix_size);
+
+ len = all_cryptmsg_encoded_len + suffix_size + dict_len;
+ BUG_ON(len > sizeof(user->buf));
+
+ *out_len = len;
+ ret = 0;
+
+out:
+ aead_request_free(aead_req);
+ return ret;
+}
+
+static ssize_t devkmsg_encrypt_onetime_piggyback_key(struct devkmsg_user *user,
+ char __user *buf)
+{
+ /*
+ * Send down the encryted session key as the first message. We identify
+ * it using the 'K:' prefix.
+ */
+ const char *prefix = "7,0,0,-;K:";
+ size_t prefix_len;
+ size_t key_len_remaining;
+ size_t len = 0;
+ char buffer[0x100];
+ char newline = '\n';
+ u8 *key_part;
+
+ if (user->crypt.encrypted_key_len == 0 || user->crypt.encrypted_key_read)
+ return 0;
+
+ prefix_len = strlen(prefix);
+
+ if (copy_to_user(buf + len, prefix, prefix_len))
+ return -EFAULT;
+
+ /* Hex-encode and copy to userspace */
+
+ len += prefix_len;
+
+ key_len_remaining = user->crypt.encrypted_key_len;
+ key_part = user->crypt.encrypted_key;
+
+ while (key_len_remaining > 0) {
+ int p = min(key_len_remaining, sizeof(buffer)/2), i, j;
+
+ for (j = 0, i = 0; i < p; j += 2, i++) {
+ u8 octet = key_part[i];
+
+ buffer[j + 0] = hex_legend[(octet >> 4) & 0xf];
+ buffer[j + 1] = hex_legend[(octet >> 0) & 0xf];
+ }
+
+ if (copy_to_user(buf + len, buffer, p * 2))
+ return -EFAULT;
+
+ key_len_remaining -= p;
+ key_part += p;
+ len += p * 2;
+ }
+
+ if (copy_to_user(buf + len, &newline, 1))
+ return -EFAULT;
+
+ len += 1;
+
+ user->crypt.encrypted_key_read = true;
+ return len;
+}
+
+static bool devkmsg_crypt_allow_syslog(void)
+{
+ return kmsg_encrypt != 0;
+}
+
+#else
+
+static void devkmsg_crypt_free(struct devkmsg_crypt *crypt) {}
+static int devkmsg_crypt_init(struct devkmsg_crypt *crypt)
+{
+ return 0;
+}
+
+static int devkmsg_encrypt_inplace(struct devkmsg_user *user,
+ size_t hdr_len, size_t len,
+ size_t *out_len)
+{
+ return 0;
+}
+
+static ssize_t devkmsg_encrypt_onetime_piggyback_key(struct devkmsg_user *user,
+ char __user *buf)
+{
+ return 0;
+}
+
+static bool devkmsg_crypt_allow_syslog(void)
+{
+ return true;
+}
+
+#endif
+
static ssize_t devkmsg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -823,6 +1179,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
struct printk_log *msg;
size_t len;
ssize_t ret;
+ int hdr_len;
if (!user)
return -EBADF;
@@ -831,6 +1188,10 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
if (ret)
return ret;
+ ret = devkmsg_encrypt_onetime_piggyback_key(user, buf);
+ if (ret != 0)
+ goto out;
+
logbuf_lock_irq();
while (user->seq == log_next_seq) {
if (file->f_flags & O_NONBLOCK) {
@@ -859,6 +1220,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
msg = log_from_idx(user->idx);
len = msg_print_ext_header(user->buf, sizeof(user->buf),
msg, user->seq);
+ hdr_len = len;
len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len,
log_dict(msg), msg->dict_len,
log_text(msg), msg->text_len);
@@ -872,10 +1234,15 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
goto out;
}
+ ret = devkmsg_encrypt_inplace(user, hdr_len, len, &len);
+ if (ret)
+ goto out;
+
if (copy_to_user(buf, user->buf, len)) {
ret = -EFAULT;
goto out;
}
+
ret = len;
out:
mutex_unlock(&user->lock);
@@ -943,6 +1310,44 @@ static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
return ret;
}
+static long devkmsg_ioctl(struct file *file, unsigned int ioctl,
+ unsigned long arg)
+{
+ switch (ioctl) {
+#ifdef CONFIG_KMSG_ENCRYPTION
+ case KMSG_IOCTL__GET_ENCRYPTED_KEY: {
+ struct devkmsg_user *user = file->private_data;
+ struct kmsg_ioctl_get_encrypted_key params;
+ int err;
+
+ if (copy_from_user(¶ms, (void __user *)arg, sizeof(params)))
+ return -EFAULT;
+
+ if (!user->crypt.encrypted_key) {
+ err = -ENOENT;
+ } else {
+ params.key_size = user->crypt.encrypted_key_len;
+
+ if (user->crypt.encrypted_key_len > params.buffer_size)
+ err = -E2BIG;
+ else
+ err = copy_to_user(params.output_buffer,
+ user->crypt.encrypted_key,
+ user->crypt.encrypted_key_len);
+ }
+
+ if (copy_to_user((void __user *)arg, ¶ms, sizeof(params)))
+ return -EFAULT;
+
+ return err;
+ }
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+
+
static int devkmsg_open(struct inode *inode, struct file *file)
{
struct devkmsg_user *user;
@@ -963,6 +1368,12 @@ static int devkmsg_open(struct inode *inode, struct file *file)
if (!user)
return -ENOMEM;
+ err = devkmsg_crypt_init(&user->crypt);
+ if (err < 0) {
+ kfree(user);
+ return err;
+ }
+
ratelimit_default_init(&user->rs);
ratelimit_set_flags(&user->rs, RATELIMIT_MSG_ON_RELEASE);
@@ -987,6 +1398,7 @@ static int devkmsg_release(struct inode *inode, struct file *file)
ratelimit_state_exit(&user->rs);
mutex_destroy(&user->lock);
+ devkmsg_crypt_free(&user->crypt);
kfree(user);
return 0;
}
@@ -997,6 +1409,7 @@ const struct file_operations kmsg_fops = {
.write_iter = devkmsg_write,
.llseek = devkmsg_llseek,
.poll = devkmsg_poll,
+ .unlocked_ioctl = devkmsg_ioctl,
.release = devkmsg_release,
};
@@ -1442,6 +1855,8 @@ int do_syslog(int type, char __user *buf, int len, int source)
case SYSLOG_ACTION_OPEN: /* Open log */
break;
case SYSLOG_ACTION_READ: /* Read from log */
+ if (!devkmsg_crypt_allow_syslog())
+ return -EPERM;
if (!buf || len < 0)
return -EINVAL;
if (!len)
@@ -1460,6 +1875,8 @@ int do_syslog(int type, char __user *buf, int len, int source)
/* FALL THRU */
/* Read last kernel messages */
case SYSLOG_ACTION_READ_ALL:
+ if (!devkmsg_crypt_allow_syslog())
+ return -EPERM;
if (!buf || len < 0)
return -EINVAL;
if (!len)
@@ -1470,6 +1887,8 @@ int do_syslog(int type, char __user *buf, int len, int source)
break;
/* Clear ring buffer */
case SYSLOG_ACTION_CLEAR:
+ if (!devkmsg_crypt_allow_syslog())
+ return -EPERM;
syslog_print_all(NULL, 0, true);
break;
/* Disable logging to console */
@@ -1497,6 +1916,9 @@ int do_syslog(int type, char __user *buf, int len, int source)
break;
/* Number of chars in the log buffer */
case SYSLOG_ACTION_SIZE_UNREAD:
+ if (!devkmsg_crypt_allow_syslog())
+ return -EPERM;
+
logbuf_lock_irq();
if (syslog_seq < log_first_seq) {
/* messages are gone, move to first one */
--
2.13.6
From: Dan Aloni <[email protected]>
Example execution:
dmesg | dmesg-decipher <private-key.pem>
Signed-off-by: Dan Aloni <[email protected]>
---
tools/Makefile | 5 +-
tools/kmsg/.gitignore | 1 +
tools/kmsg/Makefile | 14 ++
tools/kmsg/dmesg-decipher.c | 316 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 335 insertions(+), 1 deletion(-)
create mode 100644 tools/kmsg/.gitignore
create mode 100644 tools/kmsg/Makefile
create mode 100644 tools/kmsg/dmesg-decipher.c
diff --git a/tools/Makefile b/tools/Makefile
index be02c8b904db..d92d86e0227c 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -167,6 +167,9 @@ tmon_clean:
freefall_clean:
$(call descend,laptop/freefall,clean)
+kmsg:
+ $(call descend,kmsg,clean)
+
build_clean:
$(call descend,build,clean)
@@ -174,6 +177,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
- gpio_clean objtool_clean leds_clean wmi_clean
+ gpio_clean objtool_clean leds_clean wmi_clean kmsg
.PHONY: FORCE
diff --git a/tools/kmsg/.gitignore b/tools/kmsg/.gitignore
new file mode 100644
index 000000000000..a5b4e26b8d0b
--- /dev/null
+++ b/tools/kmsg/.gitignore
@@ -0,0 +1 @@
+dmesg-decipher
diff --git a/tools/kmsg/Makefile b/tools/kmsg/Makefile
new file mode 100644
index 000000000000..9f4ef7b11798
--- /dev/null
+++ b/tools/kmsg/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+CC := $(CROSS_COMPILE)gcc
+
+CFLAGS := -O2 -Wall $$(pkg-config --libs openssl)
+
+PROGS := dmesg-decipher
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+all: $(PROGS)
+
+clean:
+ rm -fr $(PROGS)
diff --git a/tools/kmsg/dmesg-decipher.c b/tools/kmsg/dmesg-decipher.c
new file mode 100644
index 000000000000..c7149fe7dc17
--- /dev/null
+++ b/tools/kmsg/dmesg-decipher.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dmesg-decipher.c
+ *
+ * A sample utility to decrypt an encrypted dmesg output, for
+ * developement with kernels having kmsg encryption enabled.
+ *
+ * Copyright (c) Dan Aloni, 2017
+ *
+ * Compile with
+ * gcc -I/usr/src/linux/include getdelays.c -o getdelays
+ */
+
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <regex.h>
+
+/*
+ * The following is based on code from:
+ *
+ * https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+ */
+static int aes_256_gcm_decrypt(unsigned char *ciphertext, size_t ciphertext_len,
+ unsigned char *aad, size_t aad_len,
+ unsigned char *tag, unsigned char *key,
+ unsigned char *iv, size_t iv_len,
+ unsigned char *plaintext)
+{
+ EVP_CIPHER_CTX *ctx;
+ int len;
+ int plaintext_len;
+ int ret = -1;
+
+ /* Create and initialise the context */
+ if (!(ctx = EVP_CIPHER_CTX_new()))
+ return -1;
+
+ /* Initialise the decryption operation. */
+ if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL))
+ goto free;
+
+ /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
+ if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL))
+ goto free;
+
+ /* Initialise key and IV */
+ if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
+ goto free;
+
+ /* Provide any AAD data. This can be called zero or more times as
+ * required
+ */
+ if (aad_len != 0) {
+ if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
+ goto free;
+ }
+
+ /* Provide the message to be decrypted, and obtain the plaintext output.
+ * EVP_DecryptUpdate can be called multiple times if necessary
+ */
+ if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext,
+ ciphertext_len))
+ goto free;
+ plaintext_len = len;
+
+ /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
+ if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
+ goto free;
+
+ /* Finalise the decryption. A positive return value indicates success,
+ * anything else is a failure - the plaintext is not trustworthy.
+ */
+ ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+
+free:
+ /* Clean up */
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (ret > 0) {
+ /* Success */
+ plaintext_len += len;
+ return plaintext_len;
+ } else {
+ /* Verify failed */
+ return -1;
+ }
+}
+
+static int hex_char_decode(char input, uint8_t *output)
+{
+ if ('a' <= input && input <= 'f')
+ *output = 10 + input - 'a';
+ else if ('0' <= input && input <= '9')
+ *output = input - '0';
+ else
+ return -1;
+
+ return 0;
+}
+
+static int hex_string_decode(const char *input, size_t ninput, void *output,
+ size_t *noutput)
+{
+ uint8_t *output_buffer = output;
+ int ret;
+ uint8_t hexval_a;
+ uint8_t hexval_b;
+
+ if (ninput % 2)
+ return -1;
+ if (ninput / 2 > *noutput)
+ return -2;
+
+ *noutput = 0;
+ while (ninput > 0) {
+ ret = hex_char_decode(input[0], &hexval_a);
+ if (ret < 0)
+ break;
+
+ ret = hex_char_decode(input[1], &hexval_b);
+ if (ret < 0)
+ break;
+
+ *output_buffer = (hexval_a << 4) | hexval_b;
+ output_buffer++;
+ *noutput += 1;
+
+ input += 2;
+ ninput -= 2;
+ }
+
+ if (ninput == 0)
+ return 0;
+
+ return -2 + ret;
+}
+
+static int parse_int_regex_match(const char *source, regmatch_t match,
+ size_t *output)
+{
+ char decimal_number[0x10] = {
+ 0,
+ };
+ size_t len = match.rm_eo - match.rm_so;
+
+ if (len >= sizeof(decimal_number))
+ return -1;
+
+ memcpy(&decimal_number[0], &source[match.rm_so], len);
+
+ *output = atoi(decimal_number);
+ return 0;
+}
+
+static const char session_key_pattern[] = "(.*)K:([0-9a-f]+)";
+static const char message_pattern[] =
+ ".*M:([0-9a-f]+),([0-9]+),([0-9]+)";
+
+static int decrypt_message(const char *line, regmatch_t *matches,
+ uint8_t *sess_key)
+{
+ char plain_text[0x1000];
+ uint8_t cipher_msg_bin[0x1000];
+ size_t cipher_msg_size = sizeof(cipher_msg_bin);
+ size_t cipher_size;
+ const regmatch_t prefix = matches[1];
+ const regmatch_t ciphermsg = matches[2];
+ const regmatch_t auth_str_len = matches[3];
+ const regmatch_t iv_str_len = matches[4];
+ size_t auth_len;
+ size_t iv_len;
+ size_t ciphertext_auth_iv_len = ciphermsg.rm_eo - ciphermsg.rm_so;
+ int ret;
+
+ ret = parse_int_regex_match(line, auth_str_len, &auth_len);
+ if (ret)
+ return -1;
+
+ ret = parse_int_regex_match(line, iv_str_len, &iv_len);
+ if (ret)
+ return -1;
+
+ ret = hex_string_decode(&line[ciphermsg.rm_so], ciphertext_auth_iv_len,
+ cipher_msg_bin, &cipher_msg_size);
+ if (ret)
+ return -1;
+
+ if (iv_len >= cipher_msg_size
+ || auth_len >= cipher_msg_size
+ || auth_len + iv_len > cipher_msg_size) {
+ return -1;
+ }
+
+ cipher_size = cipher_msg_size - auth_len - iv_len;
+
+ ret = aes_256_gcm_decrypt(/* Ciphertext */
+ (uint8_t *)cipher_msg_bin,
+ cipher_size,
+
+ /* AAD */
+ NULL,
+ 0,
+
+ /* tag */
+ (uint8_t *)&cipher_msg_bin[cipher_size],
+
+ /* key */
+ sess_key,
+
+ /* IV */
+ (uint8_t *)&cipher_msg_bin[cipher_size + auth_len], iv_len,
+
+ /* Plain text */
+ (uint8_t *)plain_text);
+ if (ret > 0) {
+ fwrite(line, prefix.rm_eo, 1, stdout);
+ fwrite(plain_text, ret, 1, stdout);
+ fwrite("\n", 1, 1, stdout);
+ }
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ BIO *tbio = NULL;
+ RSA *rsa;
+ int ret = 1;
+ char line[0x1000];
+ uint8_t enc_sess_key[0x200];
+ uint8_t sess_key[0x200] = {0, };
+ bool got_key = false;
+
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ regex_t session_key_regex;
+ regex_t message_regex;
+
+ ret = regcomp(&session_key_regex, session_key_pattern, REG_EXTENDED);
+ if (ret) {
+ goto err;
+ }
+
+ ret = regcomp(&message_regex, message_pattern, REG_EXTENDED);
+ if (ret) {
+ goto err;
+ }
+
+ if (argc < 2) {
+ fprintf(stderr, "not enough paramters\n");
+ return -1;
+ }
+
+ /* Read in recipient certificate and private key */
+ tbio = BIO_new_file(argv[1], "r");
+ if (!tbio) {
+ fprintf(stderr, "BIO_new_file - error\n");
+ goto err;
+ }
+
+ rsa = PEM_read_bio_RSAPrivateKey(tbio, NULL, NULL, NULL);
+ if (!rsa)
+ goto err;
+
+ while (true) {
+ regmatch_t matches[5];
+
+ if (!fgets(line, sizeof(line), stdin))
+ break;
+
+ if (!got_key
+ && !regexec(&session_key_regex, line, 5, matches, 0)) {
+ const regmatch_t match = matches[2];
+ size_t enc_sess_key_size = sizeof(enc_sess_key);
+
+ ret = hex_string_decode(
+ &line[match.rm_so], match.rm_eo - match.rm_so,
+ &enc_sess_key, &enc_sess_key_size);
+ if (ret)
+ goto err;
+
+ ret = RSA_private_decrypt(enc_sess_key_size,
+ enc_sess_key, sess_key, rsa,
+ RSA_PKCS1_PADDING);
+ if (ret < 0)
+ goto err;
+
+ got_key = true;
+ }
+
+ if (!regexec(&message_regex, line, 5, matches, 0)) {
+ if (!got_key) {
+ fprintf(stderr,
+ "session key must precede messages\n");
+ break;
+ }
+
+ ret = decrypt_message(line, matches, sess_key);
+ if (ret < 0) {
+ break;
+ }
+ }
+ }
+
+ regfree(&session_key_regex);
+ regfree(&message_regex);
+
+err:
+ return -1;
+}
--
2.13.6
From: Dan Aloni <[email protected]>
Signed-off-by: Dan Aloni <[email protected]>
---
Documentation/admin-guide/dmesg-encryption.rst | 77 ++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
diff --git a/Documentation/admin-guide/dmesg-encryption.rst b/Documentation/admin-guide/dmesg-encryption.rst
new file mode 100644
index 000000000000..4a3d087b7f98
--- /dev/null
+++ b/Documentation/admin-guide/dmesg-encryption.rst
@@ -0,0 +1,77 @@
+Kernel message encryption
+-------------------------
+
+.. CONTENTS
+..
+.. - Overview
+.. - Reason for encrypting dmesg
+.. - Compile time and run time switches
+.. - Limitations
+.. - Decrypting dmesg
+
+
+========
+Overview
+========
+
+Similaly the module signing facility, it is also possible to have the kernel perform public key encryption of the kernel messages that are being generated by printk calls.
+
+The encryption can be performed for one of the trusted public keys in the kernel keyring, and by default will be performed against the kernel's moduel signing key.
+
+To prevent a run-time dependency inside printk itself, the encryption takes places upon trying to read ``/dev/kmsg`` which is the mechanism currenly used by ``systemd`` to read kernel messages, and is also used by ``dmesg`` invocations.
+
+The first line being read by a ``dmesg`` opener will be an artificial line containing an encrypted symmetric encryption session key, in RSA PKCS#1 format. The other lines are messages encrypted under an AES-128-GCM scheme. All binary ciphertext is hex-encoded, so that the ciphertext solely comprises of printable characters.
+
+===========
+Limitations
+===========
+
+There are various limitations one need to consider when enabling dmesg encryption:
+
+ * The metadata of kernel messages is not part of the encryption (timestamp, log facility, log severity).
+ * The seldom accompanying dictionary is also not part of the encryption.
+ * Any output to any system console, happening when printk() itself is executing, is also not encrypted. A potential attacker can load up ``netconsole`` and have kernel messages being sent as plaintext to other machines. Hopefully, on embedded devices, all system consoles are under strict control of the developers.
+ * The syslog system call is barred from reading kmsg. Its present users are few, as the system call's interface is mostly a fallback to an inaccessible ``/dev/kmsg``. This is only an implementation limitation and that may be addressed.
+ * kmsg buffers will still be saved as plaintext inside kdumps. The assumption is that having an access to read a kdump is equivalent to full kernel access anyway.
+
+===========================
+Reason for encryption dmesg
+===========================
+
+For years, dmesg has contained data which could be utilized by vulnerability exploiters, allowing for privilege escalations. Developers may leave key data such as pointers, indication of driver bugs, and more.
+
+The feature is mostly aimed for device manufacturers who are not keen on revealing the full details of kernel execution, bugs, and crashes to their users, but only to their developers, so that local programs running on the devices cannot use the data for 'rooting' and executing exploits.
+
+==================================
+Compile time and run time switches
+==================================
+
+In build time, this feature is controlled via the ``CONFIG_KMSG_ENCRYPTION`` configuration variable.
+
+In run time, it can be turned off by providing `kmsg.encrypt=0` as a boot time parameter.
+
+================
+Decrypting dmesg
+================
+
+A supplied program in the kernel tree named ``dmesg-decipher``, uses the OpenSSL library along with the paired private key of the encryption, in order to decipher an encrypted dmesg.
+
+An innocuous dmesg invocation will appear as such (with the ciphertexts shortened here for the brevity of this document)::
+
+ [ 0.000000] K:7c7da3617b6f...f4098e2425af00
+ [ 0.000000] M:36ae318532e...61686821b3ab,16,12
+ [ 0.000000] M:7fca10...154e035,16,12
+ ....
+
+The artificial ``K:`` message is generated per opening of ``/dev/kmsg``. It contains the encrypted session key. The encrypted dmesg lines follows it (prefix ``M:``).
+
+Provided with the private key, deciphering a dmesg output should be a straight-forward process.
+
+For example, one can save an encrypted dmesg to ``dmesg.enc`` in one machine, then transfer it to another machine which contains access to the PEM with the decrypting private key, and use the the following command::
+
+ cat dmesg.enc | ./tools/kmsg/dmesg-decipher certs/signing_key.pem
+
+ [ 0.000000] Linux version 4.15.0-rc5+ (dan@jupiter) (gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC)) #109 SMP Sat Dec 30 18:32:25 IST 2017
+ [ 0.000000] Command line: BOOT_IMAGE=/vmlinuz-4.15.0-rc5-dan+ root=UUID=f48b37ec-fcb8-4689-b12e-58703db3cb21 ro rhgb quiet LANG=en_US.UTF-8
+ [ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
+ ...
--
2.13.6
From: Dan Aloni <[email protected]>
Signed-off-by: Dan Aloni <[email protected]>
---
certs/system_keyring.c | 56 ++++++++++++++++++++++++++++++++++++++++++-
include/keys/system_keyring.h | 3 +++
2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 6251d1b27f0c..ff7c18d8e67c 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -131,6 +131,8 @@ static __init int system_trusted_keyring_init(void)
*/
device_initcall(system_trusted_keyring_init);
+static char *first_asymmetric_key_description;
+
/*
* Load the compiled-in list of X.509 certificates.
*/
@@ -172,8 +174,11 @@ static __init int load_system_certificate_list(void)
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
PTR_ERR(key));
} else {
+ first_asymmetric_key_description =
+ kstrdup(key_ref_to_ptr(key)->description,
+ GFP_KERNEL);
pr_notice("Loaded X.509 cert '%s'\n",
- key_ref_to_ptr(key)->description);
+ first_asymmetric_key_description);
key_ref_put(key);
}
p += plen;
@@ -265,3 +270,52 @@ int verify_pkcs7_signature(const void *data, size_t len,
EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
+
+/**
+ * get_first_asymmetric_key - Find a key by ID.
+ * @keyring: The keys to search.
+ *
+ * Return the first assymmetric key in a keyring.
+ */
+static struct key *get_first_asymmetric_key(struct key *keyring)
+{
+ key_ref_t ref;
+
+ ref = keyring_search(make_key_ref(keyring, 1),
+ &key_type_asymmetric,
+ first_asymmetric_key_description);
+ if (IS_ERR(ref)) {
+ switch (PTR_ERR(ref)) {
+ case -EACCES:
+ case -ENOTDIR:
+ case -EAGAIN:
+ return ERR_PTR(-ENOKEY);
+ default:
+ return ERR_CAST(ref);
+ }
+ }
+
+ return key_ref_to_ptr(ref);
+}
+
+/**
+ * find_asymmetric_key - Find a key by ID in the builtin trusted keys
+ * keyring, or return the first key in that keyring.
+ *
+ * @id_0: The first ID to look for or NULL.
+ * @id_1: The second ID to look for or NULL.
+ *
+ * The preferred identifier is the id_0 and the fallback identifier is
+ * the id_1. If both are given, the lookup is by the former, but the
+ * latter must also match. If none are given, the first key is returned.
+ */
+struct key *find_trusted_asymmetric_key(const struct asymmetric_key_id *id_0,
+ const struct asymmetric_key_id *id_1)
+{
+ struct key *keyring = builtin_trusted_keys;
+ if (!id_0 && !id_1) {
+ return get_first_asymmetric_key(keyring);
+ }
+
+ return find_asymmetric_key(keyring, id_0, id_1, false);
+}
diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h
index 359c2f936004..0bef29eb8297 100644
--- a/include/keys/system_keyring.h
+++ b/include/keys/system_keyring.h
@@ -13,6 +13,7 @@
#define _KEYS_SYSTEM_KEYRING_H
#include <linux/key.h>
+#include <keys/asymmetric-type.h>
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
@@ -61,5 +62,7 @@ static inline struct key *get_ima_blacklist_keyring(void)
}
#endif /* CONFIG_IMA_BLACKLIST_KEYRING */
+struct key *find_trusted_asymmetric_key(const struct asymmetric_key_id *id_0,
+ const struct asymmetric_key_id *id_1);
#endif /* _KEYS_SYSTEM_KEYRING_H */
--
2.13.6
On 12/30/2017 09:58 AM, Dan Aloni wrote:
> From: Dan Aloni <[email protected]>
>
> Signed-off-by: Dan Aloni <[email protected]>
> ---
> certs/system_keyring.c | 56 ++++++++++++++++++++++++++++++++++++++++++-
> include/keys/system_keyring.h | 3 +++
> 2 files changed, 58 insertions(+), 1 deletion(-)
>
> diff --git a/certs/system_keyring.c b/certs/system_keyring.c
> index 6251d1b27f0c..ff7c18d8e67c 100644
> --- a/certs/system_keyring.c
> +++ b/certs/system_keyring.c
> @@ -131,6 +131,8 @@ static __init int system_trusted_keyring_init(void)
> */
> device_initcall(system_trusted_keyring_init);
>
> +static char *first_asymmetric_key_description;
> +
> /*
> * Load the compiled-in list of X.509 certificates.
> */
> @@ -172,8 +174,11 @@ static __init int load_system_certificate_list(void)
> pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
> PTR_ERR(key));
> } else {
> + first_asymmetric_key_description =
> + kstrdup(key_ref_to_ptr(key)->description,
> + GFP_KERNEL);
> pr_notice("Loaded X.509 cert '%s'\n",
> - key_ref_to_ptr(key)->description);
> + first_asymmetric_key_description);
> key_ref_put(key);
> }
> p += plen;
> @@ -265,3 +270,52 @@ int verify_pkcs7_signature(const void *data, size_t len,
> EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
>
> #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
> +
> +/**
> + * get_first_asymmetric_key - Find a key by ID.
> + * @keyring: The keys to search.
> + *
> + * Return the first assymmetric key in a keyring.
spello
> + */
> +static struct key *get_first_asymmetric_key(struct key *keyring)
> +{
> + key_ref_t ref;
> +
> + ref = keyring_search(make_key_ref(keyring, 1),
> + &key_type_asymmetric,
> + first_asymmetric_key_description);
> + if (IS_ERR(ref)) {
> + switch (PTR_ERR(ref)) {
> + case -EACCES:
> + case -ENOTDIR:
> + case -EAGAIN:
> + return ERR_PTR(-ENOKEY);
> + default:
> + return ERR_CAST(ref);
> + }
> + }
> +
> + return key_ref_to_ptr(ref);
> +}
> +
> +/**
> + * find_asymmetric_key - Find a key by ID in the builtin trusted keys
^Function name should match the name of the function below.
> + * keyring, or return the first key in that keyring.
> + *
> + * @id_0: The first ID to look for or NULL.
> + * @id_1: The second ID to look for or NULL.
> + *
> + * The preferred identifier is the id_0 and the fallback identifier is
> + * the id_1. If both are given, the lookup is by the former, but the
> + * latter must also match. If none are given, the first key is returned.
> + */
> +struct key *find_trusted_asymmetric_key(const struct asymmetric_key_id *id_0,
> + const struct asymmetric_key_id *id_1)
> +{
> + struct key *keyring = builtin_trusted_keys;
> + if (!id_0 && !id_1) {
> + return get_first_asymmetric_key(keyring);
> + }
> +
> + return find_asymmetric_key(keyring, id_0, id_1, false);
> +}
--
~Randy
On Sat, Dec 30, 2017 at 12:58 PM, Dan Aloni <[email protected]> wrote:
> From: Dan Aloni <[email protected]>
>
> Signed-off-by: Dan Aloni <[email protected]>
> ---
> Documentation/admin-guide/dmesg-encryption.rst | 77 ++++++++++++++++++++++++++
> 1 file changed, 77 insertions(+)
> create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
>
> diff --git a/Documentation/admin-guide/dmesg-encryption.rst b/Documentation/admin-guide/dmesg-encryption.rst
> new file mode 100644
> index 000000000000..4a3d087b7f98
> --- /dev/null
> +++ b/Documentation/admin-guide/dmesg-encryption.rst
> @@ -0,0 +1,77 @@
> +Kernel message encryption
> +-------------------------
> +
> +.. CONTENTS
> +..
> +.. - Overview
> +.. - Reason for encrypting dmesg
> +.. - Compile time and run time switches
> +.. - Limitations
> +.. - Decrypting dmesg
> +
> +
> +========
> +Overview
> +========
> +
> +Similaly the module signing facility, it is also possible to have the kernel perform public key encryption of the kernel messages that are being generated by printk calls.
> +
> +The encryption can be performed for one of the trusted public keys in the kernel keyring, and by default will be performed against the kernel's moduel signing key.
Typo - s/moduel/module/
> +
> +To prevent a run-time dependency inside printk itself, the encryption takes places upon trying to read ``/dev/kmsg`` which is the mechanism currenly used by ``systemd`` to read kernel messages, and is also used by ``dmesg`` invocations.
Typo - s/currenly/currently/
> +
> +The first line being read by a ``dmesg`` opener will be an artificial line containing an encrypted symmetric encryption session key, in RSA PKCS#1 format. The other lines are messages encrypted under an AES-128-GCM scheme. All binary ciphertext is hex-encoded, so that the ciphertext solely comprises of printable characters.
> +
> +===========
> +Limitations
> +===========
> +
> +There are various limitations one need to consider when enabling dmesg encryption:
> +
> + * The metadata of kernel messages is not part of the encryption (timestamp, log facility, log severity).
> + * The seldom accompanying dictionary is also not part of the encryption.
> + * Any output to any system console, happening when printk() itself is executing, is also not encrypted. A potential attacker can load up ``netconsole`` and have kernel messages being sent as plaintext to other machines. Hopefully, on embedded devices, all system consoles are under strict control of the developers.
> + * The syslog system call is barred from reading kmsg. Its present users are few, as the system call's interface is mostly a fallback to an inaccessible ``/dev/kmsg``. This is only an implementation limitation and that may be addressed.
> + * kmsg buffers will still be saved as plaintext inside kdumps. The assumption is that having an access to read a kdump is equivalent to full kernel access anyway.
> +
> +===========================
> +Reason for encryption dmesg
> +===========================
> +
> +For years, dmesg has contained data which could be utilized by vulnerability exploiters, allowing for privilege escalations. Developers may leave key data such as pointers, indication of driver bugs, and more.
> +
> +The feature is mostly aimed for device manufacturers who are not keen on revealing the full details of kernel execution, bugs, and crashes to their users, but only to their developers, so that local programs running on the devices cannot use the data for 'rooting' and executing exploits.
> +
> +==================================
> +Compile time and run time switches
> +==================================
> +
> +In build time, this feature is controlled via the ``CONFIG_KMSG_ENCRYPTION`` configuration variable.
> +
> +In run time, it can be turned off by providing `kmsg.encrypt=0` as a boot time parameter.
> +
> +================
> +Decrypting dmesg
> +================
> +
> +A supplied program in the kernel tree named ``dmesg-decipher``, uses the OpenSSL library along with the paired private key of the encryption, in order to decipher an encrypted dmesg.
> +
> +An innocuous dmesg invocation will appear as such (with the ciphertexts shortened here for the brevity of this document)::
> +
> + [ 0.000000] K:7c7da3617b6f...f4098e2425af00
> + [ 0.000000] M:36ae318532e...61686821b3ab,16,12
> + [ 0.000000] M:7fca10...154e035,16,12
> + ....
> +
> +The artificial ``K:`` message is generated per opening of ``/dev/kmsg``. It contains the encrypted session key. The encrypted dmesg lines follows it (prefix ``M:``).
> +
> +Provided with the private key, deciphering a dmesg output should be a straight-forward process.
> +
> +For example, one can save an encrypted dmesg to ``dmesg.enc`` in one machine, then transfer it to another machine which contains access to the PEM with the decrypting private key, and use the the following command::
> +
> + cat dmesg.enc | ./tools/kmsg/dmesg-decipher certs/signing_key.pem
> +
> + [ 0.000000] Linux version 4.15.0-rc5+ (dan@jupiter) (gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC)) #109 SMP Sat Dec 30 18:32:25 IST 2017
> + [ 0.000000] Command line: BOOT_IMAGE=/vmlinuz-4.15.0-rc5-dan+ root=UUID=f48b37ec-fcb8-4689-b12e-58703db3cb21 ro rhgb quiet LANG=en_US.UTF-8
> + [ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
> + ...
> --
> 2.13.6
>
The proposed use case for embedded developers and other constrained
systems makes me wonder how useful a GPL-only facility is in those
situations. AFAIK the Linux crypto subsystem is pretty restrictive for
non-GPL modules (Tom Caputi of Datto had to port over a ton of Illumos
crypto code for ZFS), and third-party components which can't be
released under GPL also shouldn't be linking against GPL-only
functions. I'm woefully unfamiliar with the history and context around
decisions to mark things GPL-only, so please pardon my ignorance on
the matter, but in practice this might be a barrier to legal
real-world use.
Another use case for this, by the way, is for private clouds where
instance owners may be untrusted and the hypervisor or cloud fabric
have access to the decryption keys as well as out-of-band dmesg
outputs since they hook ttySX in the VM and spool to a buffer outside
the instance. Bugs in virtual devices mapping physical memory would
produce usable output for the cloud provider to track and tackle,
while increasing the barriers toward mapping host resources from
inside the instance. Obviously an attacker with root access can bypass
this facility with a reboot or kernel replacement, but it does raise
the barrier and forces them to leave footprints like an elephant.
Clouds running containers as the isolation mechanism and sharing a
kernel benefit all the more by reducing cross-container leakage, and
without the kernel concern (if they're escaping the container to alter
that, we're long past this problem domain). Lastly, private instances
in public clouds should also benefit from this as the same semantic
around dumping dmesg to an externally facing serial tty is standard
fare, and the provider can currently see tons of (ideally) privileged
data.
--
Boris Lukashev
Systems Architect
Semper Victus
On 12/30/2017 09:58 AM, Dan Aloni wrote:
> From: Dan Aloni <[email protected]>
>
> Signed-off-by: Dan Aloni <[email protected]>
> ---
> Documentation/admin-guide/dmesg-encryption.rst | 77 ++++++++++++++++++++++++++
> 1 file changed, 77 insertions(+)
> create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
>
> diff --git a/Documentation/admin-guide/dmesg-encryption.rst b/Documentation/admin-guide/dmesg-encryption.rst
> new file mode 100644
> index 000000000000..4a3d087b7f98
> --- /dev/null
> +++ b/Documentation/admin-guide/dmesg-encryption.rst
> @@ -0,0 +1,77 @@
> +Kernel message encryption
> +-------------------------
> +
> +.. CONTENTS
> +..
> +.. - Overview
> +.. - Reason for encrypting dmesg
> +.. - Compile time and run time switches
> +.. - Limitations
> +.. - Decrypting dmesg
> +
> +
> +========
> +Overview
> +========
> +
Please hard wrap long lines so that they are readable in 80 columns. Some people
still read .rst files like they are text files.
> +Similaly the module signing facility, it is also possible to have the kernel perform public key encryption of the kernel messages that are being generated by printk calls.
Similar to the module signing facility,
> +
> +The encryption can be performed for one of the trusted public keys in the kernel keyring, and by default will be performed against the kernel's moduel signing key.
s/moduel/module/
> +
> +To prevent a run-time dependency inside printk itself, the encryption takes places upon trying to read ``/dev/kmsg`` which is the mechanism currenly used by ``systemd`` to read kernel messages, and is also used by ``dmesg`` invocations.
s/takes places/takes place/
s/currenly/currently/
> +
> +The first line being read by a ``dmesg`` opener will be an artificial line containing an encrypted symmetric encryption session key, in RSA PKCS#1 format. The other lines are messages encrypted under an AES-128-GCM scheme. All binary ciphertext is hex-encoded, so that the ciphertext solely comprises of printable characters.
> +
> +===========
> +Limitations
> +===========
> +
> +There are various limitations one need to consider when enabling dmesg encryption:
needs
> +
> + * The metadata of kernel messages is not part of the encryption (timestamp, log facility, log severity).
> + * The seldom accompanying dictionary is also not part of the encryption.
> + * Any output to any system console, happening when printk() itself is executing, is also not encrypted. A potential attacker can load up ``netconsole`` and have kernel messages being sent as plaintext to other machines. Hopefully, on embedded devices, all system consoles are under strict control of the developers.
> + * The syslog system call is barred from reading kmsg. Its present users are few, as the system call's interface is mostly a fallback to an inaccessible ``/dev/kmsg``. This is only an implementation limitation and that may be addressed.
> + * kmsg buffers will still be saved as plaintext inside kdumps. The assumption is that having an access to read a kdump is equivalent to full kernel access anyway.
> +
> +===========================
> +Reason for encryption dmesg
> +===========================
> +
> +For years, dmesg has contained data which could be utilized by vulnerability exploiters, allowing for privilege escalations. Developers may leave key data such as pointers, indication of driver bugs, and more.
> +
> +The feature is mostly aimed for device manufacturers who are not keen on revealing the full details of kernel execution, bugs, and crashes to their users, but only to their developers, so that local programs running on the devices cannot use the data for 'rooting' and executing exploits.
> +
> +==================================
> +Compile time and run time switches
> +==================================
> +
> +In build time, this feature is controlled via the ``CONFIG_KMSG_ENCRYPTION`` configuration variable.
> +
> +In run time, it can be turned off by providing `kmsg.encrypt=0` as a boot time parameter.
A dot ('.') in a kernel parameter name usually means <module_name>.<module_parameter_name>.
This probably does work as it is here, but it's unusual. It would be better to change
it to an underscore: kmsg_encrypt=0
or since it is printk.c, it could be: printk.kmsg_encrypt=0
depending on how it is implemented (__setup vs. module_param_* or core_param).
> +
> +================
> +Decrypting dmesg
> +================
> +
> +A supplied program in the kernel tree named ``dmesg-decipher``, uses the OpenSSL library
no comma after ``dmesg-decipher``
along with the paired private key of the encryption, in order to decipher an encrypted dmesg.
no comma after "encryption"
> +
> +An innocuous dmesg invocation will appear as such (with the ciphertexts shortened here for the brevity of this document)::
> +
> + [ 0.000000] K:7c7da3617b6f...f4098e2425af00
> + [ 0.000000] M:36ae318532e...61686821b3ab,16,12
> + [ 0.000000] M:7fca10...154e035,16,12
> + ....
> +
> +The artificial ``K:`` message is generated per opening of ``/dev/kmsg``. It contains the encrypted session key. The encrypted dmesg lines follows it (prefix ``M:``).
> +
> +Provided with the private key, deciphering a dmesg output should be a straight-forward process.
"straightforward"
> +
> +For example, one can save an encrypted dmesg to ``dmesg.enc`` in one machine, then transfer it to another machine which contains access to the PEM with the decrypting private key, and use the the following command::
> +
> + cat dmesg.enc | ./tools/kmsg/dmesg-decipher certs/signing_key.pem
> +
> + [ 0.000000] Linux version 4.15.0-rc5+ (dan@jupiter) (gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC)) #109 SMP Sat Dec 30 18:32:25 IST 2017
> + [ 0.000000] Command line: BOOT_IMAGE=/vmlinuz-4.15.0-rc5-dan+ root=UUID=f48b37ec-fcb8-4689-b12e-58703db3cb21 ro rhgb quiet LANG=en_US.UTF-8
> + [ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
> + ...
>
--
~Randy
On 12/30/2017 09:58 AM, Dan Aloni wrote:
> From: Dan Aloni <[email protected]>
>
> Example execution:
>
> dmesg | dmesg-decipher <private-key.pem>
>
> Signed-off-by: Dan Aloni <[email protected]>
> ---
> diff --git a/tools/kmsg/dmesg-decipher.c b/tools/kmsg/dmesg-decipher.c
> new file mode 100644
> index 000000000000..c7149fe7dc17
> --- /dev/null
> +++ b/tools/kmsg/dmesg-decipher.c
> @@ -0,0 +1,316 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * dmesg-decipher.c
> + *
> + * A sample utility to decrypt an encrypted dmesg output, for
> + * developement with kernels having kmsg encryption enabled.
> + *
> + * Copyright (c) Dan Aloni, 2017
> + *
> + * Compile with
> + * gcc -I/usr/src/linux/include getdelays.c -o getdelays
copy-paste error ^^^
> + */
> +
> +#include <openssl/pem.h>
> +#include <openssl/pkcs7.h>
> +#include <openssl/err.h>
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <regex.h>
[snip]
> +int main(int argc, char **argv)
> +{
> + BIO *tbio = NULL;
> + RSA *rsa;
> + int ret = 1;
> + char line[0x1000];
> + uint8_t enc_sess_key[0x200];
> + uint8_t sess_key[0x200] = {0, };
> + bool got_key = false;
> +
> + OpenSSL_add_all_algorithms();
> + ERR_load_crypto_strings();
> +
> + regex_t session_key_regex;
> + regex_t message_regex;
> +
> + ret = regcomp(&session_key_regex, session_key_pattern, REG_EXTENDED);
> + if (ret) {
> + goto err;
> + }
> +
> + ret = regcomp(&message_regex, message_pattern, REG_EXTENDED);
> + if (ret) {
> + goto err;
> + }
> +
> + if (argc < 2) {
> + fprintf(stderr, "not enough paramters\n");
parameters
> + return -1;
> + }
--
~Randy
On 12/30/2017 09:58 AM, Dan Aloni wrote:
> From: Dan Aloni <[email protected]>
>
> This commit enables the kernel to encrypt the free-form text that
> is generated by printk() before it is brought up to `dmesg` in
> userspace.
>
> The encryption is made using one of the trusted public keys which
> are kept built-in inside the kernel. These keys are presently
> also used for verifying kernel modules and userspace-supplied
> firmwares.
>
> Signed-off-by: Dan Aloni <[email protected]>
> ---
> include/uapi/linux/kmsg.h | 18 ++
> init/Kconfig | 10 ++
> kernel/printk/printk.c | 422 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 450 insertions(+)
> create mode 100644 include/uapi/linux/kmsg.h
>
> diff --git a/include/uapi/linux/kmsg.h b/include/uapi/linux/kmsg.h
> new file mode 100644
> index 000000000000..ae74f026d727
> --- /dev/null
> +++ b/include/uapi/linux/kmsg.h
> @@ -0,0 +1,18 @@
> +#ifndef _LINUX_UAPI_KMSG_H
> +#define _LINUX_UAPI_KMSG_H
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +struct kmsg_ioctl_get_encrypted_key {
> + void __user *output_buffer;
> + __u64 buffer_size;
> + __u64 key_size;
> +};
> +
> +#define KMSG_IOCTL_BASE 0x42
> +
> +#define KMSG_IOCTL__GET_ENCRYPTED_KEY _IOWR(KMSG_IOCTL_BASE, 0xe1, \
> + struct kmsg_ioctl_get_encrypted_key)
> +
The ioctl info needs to be added to Documentation/ioctl/ioctl-number.txt.
BTW, 0x42 == 'B', with number 0xe1, conflicts with advanced bbus, whatever
that is. But at least it should be added to the doc file.
> +#endif /* _LINUX_DN_H */
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index b9006617710f..c50b9cb60b82 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -744,12 +755,33 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
> return p - buf;
> }
>
> +#ifdef CONFIG_KMSG_ENCRYPTION
> +static int __ro_after_init kmsg_encrypt = 1;
> +static int __init control_kmsg_encrypt(char *str)
> +{
> + get_option(&str, &kmsg_encrypt);
> + return 0;
> +}
> +__setup("kmsg.encrypt=", control_kmsg_encrypt);
See comment in the documentation patch: please change the parameter name (no dot).
--
~Randy
On Sat, Dec 30, 2017 at 6:57 PM, Dan Aloni <[email protected]> wrote:
> From: Dan Aloni <[email protected]>
>
> Hi All,
>
> There has been a lot of progress in recent times regarding the removal
> of sensitive information from dmesg (pointers, etc.), so I figured - why
> not encrypt it all? However, I have not found any existing discussions
> or references regarding this technical direction.
>
> I am not sure that desktop and power users would like to have their
> kernel message encrypted, but there are scenarios such as in mobile
> devices, where only the developers, makers of devices, may actually
> benefit from access to kernel prints messages, and the users may be
> more protected from exploits.
What is the benefit of your approach compared to setting
dmesg_restrict=1 or something like that and letting userland decide
who should get access to raw dmesg output and in what form?
On Sat, Dec 30, 2017 at 11:40:06AM -0800, Randy Dunlap wrote:
> On 12/30/2017 09:58 AM, Dan Aloni wrote:
> > From: Dan Aloni <[email protected]>
> >
> > Signed-off-by: Dan Aloni <[email protected]>
> > ---
> > Documentation/admin-guide/dmesg-encryption.rst | 77 ++++++++++++++++++++++++++
> > 1 file changed, 77 insertions(+)
> > create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
> > +========
> > +Overview
> > +========
> > +
>
> Please hard wrap long lines so that they are readable in 80 columns. Some people
> still read .rst files like they are text files.
Thanks very much for all the reviews in this reply and others, your
suggestions are incorporated in the next version. A reviewed-by is in
order?
--
Dan Aloni
On Sat, Dec 30, 2017 at 10:42:49PM +0100, Jann Horn wrote:
> On Sat, Dec 30, 2017 at 6:57 PM, Dan Aloni <[email protected]> wrote:
> > From: Dan Aloni <[email protected]>
> >
> > Hi All,
> >
> > There has been a lot of progress in recent times regarding the removal
> > of sensitive information from dmesg (pointers, etc.), so I figured - why
> > not encrypt it all? However, I have not found any existing discussions
> > or references regarding this technical direction.
> >
> > I am not sure that desktop and power users would like to have their
> > kernel message encrypted, but there are scenarios such as in mobile
> > devices, where only the developers, makers of devices, may actually
> > benefit from access to kernel prints messages, and the users may be
> > more protected from exploits.
>
> What is the benefit of your approach compared to setting
> dmesg_restrict=1 or something like that and letting userland decide
> who should get access to raw dmesg output and in what form?
Inter-process vulnerabilities (via sockets, shared memory, etc.) can
still allow one process with lesser dmesg privileges to exploit another
with higher privileges. Once the higher process is exploited, without
this approach, a plaintext dmesg is exposed for kernel exploitation
through it.
There can be systems designed in such a way that the most privileged
userland process would still be barred from accessing the raw hardware
directly, and in those systems we still want to guard the kernel from
exploits, in order to keep the hardware protected, but still allow
developers to debug the kernel.
--
Dan Aloni
On 01/03/2018 12:21 PM, Dan Aloni wrote:
> On Sat, Dec 30, 2017 at 11:40:06AM -0800, Randy Dunlap wrote:
>> On 12/30/2017 09:58 AM, Dan Aloni wrote:
>>> From: Dan Aloni <[email protected]>
>>>
>>> Signed-off-by: Dan Aloni <[email protected]>
>>> ---
>>> Documentation/admin-guide/dmesg-encryption.rst | 77 ++++++++++++++++++++++++++
>>> 1 file changed, 77 insertions(+)
>>> create mode 100644 Documentation/admin-guide/dmesg-encryption.rst
>>> +========
>>> +Overview
>>> +========
>>> +
>>
>> Please hard wrap long lines so that they are readable in 80 columns. Some people
>> still read .rst files like they are text files.
>
> Thanks very much for all the reviews in this reply and others, your
> suggestions are incorporated in the next version. A reviewed-by is in
> order?
>
only for patch 5/5: docs:
Reviewed-by: Randy Dunlap <[email protected]>
--
~Randy
On Sat 2017-12-30 19:57:59, Dan Aloni wrote:
> From: Dan Aloni <[email protected]>
>
> Hi All,
>
> There has been a lot of progress in recent times regarding the removal
> of sensitive information from dmesg (pointers, etc.), so I figured - why
> not encrypt it all? However, I have not found any existing discussions
> or references regarding this technical direction.
>
> I am not sure that desktop and power users would like to have their
> kernel message encrypted, but there are scenarios such as in mobile
> devices, where only the developers, makers of devices, may actually
> benefit from access to kernel prints messages, and the users may be
> more protected from exploits.
Yes, we have "TiVo" problem in mobile space, but please no, don't make
it worse.
We should not make it easy for device makers to lock devices down from
their owners.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html