From patchwork Wed Sep 12 14:23:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chun-Yi Lee X-Patchwork-Id: 10597635 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 579E914E5 for ; Wed, 12 Sep 2018 14:24:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 43F4F29CB1 for ; Wed, 12 Sep 2018 14:24:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 37EF529CBB; Wed, 12 Sep 2018 14:24:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A15812A164 for ; Wed, 12 Sep 2018 14:24:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726821AbeILT3B (ORCPT ); Wed, 12 Sep 2018 15:29:01 -0400 Received: from mail-pf1-f193.google.com ([209.85.210.193]:34392 "EHLO mail-pf1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726672AbeILT3B (ORCPT ); Wed, 12 Sep 2018 15:29:01 -0400 Received: by mail-pf1-f193.google.com with SMTP id k19-v6so1112472pfi.1; Wed, 12 Sep 2018 07:24:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=a/D8T2EiIp3s8RtmpQwWyb4FwC0Yguv2ZrpvEhu0bQ8=; b=sgNMoEDneEH/vF5fgZGVe5YT7wybQdEY4m7XyUfSV7yraM71SqH8bSgkcVm4OyIr+V WZWIRXo4NX+rODjo6ncKgXiNScyURkZbk/xwYR671MRb0tSzVH/hv0I/4TMuOHkX1Qb7 /LweRuDn3iBBN8mB61GfzSQwDMMVGliXXWBP2IZUp4DHLRGCbIecQ2BSW/CwvSjVEcrH fnKSJpMx5MArkRrGNjLijXROs+a2fKEhCIBhmM55BjG2Vky70BJphurkamWIqaJAvQHf UwaB/M+O2GhhEkZZ88eNJeLAe9lW3toKpVouArh/5QIh1Ykk5XWlbFe6npUP7tYP/HVN kiXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=a/D8T2EiIp3s8RtmpQwWyb4FwC0Yguv2ZrpvEhu0bQ8=; b=JS548TUXuPhuGlpuaB+B+KNKRWyOTT051RIzBy7BuFhYLdj2GQAR84S26xYS4mW3N9 piTDUEAJ6w8w0avNb8zK08JruRaLyiG56A20d24nb8/Zmf2NRn+t5XKWHBSUiGt4nQii Iwzk84KKPfbAebiZJH1YR5AobMeVb1a9IP9zIEBZQns3aB3k/0Em3Vtg/v4PhmjR4ACG 6eoFjK7AQO2wFu5fZRZWNnN6O7uAmN4CjXEmyq8csKO9m5+D2TetOdF41PLvRTEsFwWu B4QJyCi/KNvXJVtdBK050+DRm7gJy1KLoJhrQXv97GzX/zyrk/u5exL/UF1L+H4GV8td 690w== X-Gm-Message-State: APzg51B1jIucoD98R8yqY43MYKqC6ATS8N06j31KAv2Fq+uoJxOw/cU7 PzEKXSsXHqWQRi7bfWMKhC8= X-Google-Smtp-Source: ANB0VdYZIECnw+O0tAaEh5eGe2Tted8K9jOVN1R3UakhNiHt4ALz71uaSRttamOCVa7yJBOu+0uqTg== X-Received: by 2002:a65:608b:: with SMTP id t11-v6mr2636190pgu.259.1536762256969; Wed, 12 Sep 2018 07:24:16 -0700 (PDT) Received: from linux-l9pv.suse ([124.11.22.254]) by smtp.gmail.com with ESMTPSA id t9-v6sm2124213pgi.87.2018.09.12.07.24.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Sep 2018 07:24:16 -0700 (PDT) From: "Lee, Chun-Yi" X-Google-Original-From: "Lee, Chun-Yi" To: "Rafael J . Wysocki" , Pavel Machek Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: [PATCH 1/5] PM / hibernate: Create snapshot keys handler Date: Wed, 12 Sep 2018 22:23:33 +0800 Message-Id: <20180912142337.21955-2-jlee@suse.com> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180912142337.21955-1-jlee@suse.com> References: <20180912142337.21955-1-jlee@suse.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds a snapshot keys handler for using the key retention service api to create keys for snapshot image encryption and authentication. This handler uses TPM trusted key as the snapshot master key, and the encryption key and authentication key are derived from the snapshot key. The user defined key can also be used as the snapshot master key , but user must be aware that the security of user key relies on user space. The name of snapshot key is fixed to "swsusp-kmk". User should use the keyctl tool to load the key blob to root's user keyring. e.g. # /bin/keyctl add trusted swsusp-kmk "load `cat swsusp-kmk.blob`" @u or create a new user key. e.g. # /bin/keyctl add user swsusp-kmk password @u Then the disk_kmk sysfs file can be used to trigger the initialization of snapshot key: # echo 1 > /sys/power/disk_kmk After the initialization be triggered, the secret in the payload of swsusp-key will be copied by hibernation and be erased. Then user can use keyctl to remove swsusp-kmk key from root's keyring. If user does not trigger the initialization by disk_kmk file after swsusp-kmk be loaded to kernel. Then the snapshot key will be initialled when hibernation be triggered. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/Kconfig | 14 +++ kernel/power/Makefile | 1 + kernel/power/hibernate.c | 36 +++++++ kernel/power/power.h | 16 +++ kernel/power/snapshot_key.c | 237 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 kernel/power/snapshot_key.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3a6c2f87699e..7c5c30149dbc 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -76,6 +76,20 @@ config HIBERNATION For more information take a look at . +config HIBERNATION_ENC_AUTH + bool "Hibernation encryption and authentication" + depends on HIBERNATION + depends on TRUSTED_KEYS + select CRYPTO_AES + select CRYPTO_HMAC + select CRYPTO_SHA512 + help + This option will encrypt and authenticate the memory snapshot image + of hibernation. It prevents that the snapshot image be arbitrary + modified. User can use TPMs trusted key or user defined key as the + master key of hibernation. The TPM trusted key depends on TPM. The + security of user defined key relies on user space. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index a3f79f0eef36..bddca7b79a28 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o +obj-$(CONFIG_HIBERNATION_ENC_AUTH) += snapshot_key.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index abef759de7c8..18d13cbf0591 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1034,6 +1034,39 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(disk); +#ifdef CONFIG_HIBERNATION_ENC_AUTH +static ssize_t disk_kmk_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + if (snapshot_key_initialized()) + return sprintf(buf, "initialized\n"); + else + return sprintf(buf, "uninitialized\n"); +} + +static ssize_t disk_kmk_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = 0; + char *p; + int len; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + if (strncmp(buf, "1", len)) + return -EINVAL; + + error = snapshot_key_init(); + + return error ? error : n; +} + +power_attr(disk_kmk); +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1138,6 +1171,9 @@ power_attr(reserved_size); static struct attribute * g[] = { &disk_attr.attr, +#ifdef CONFIG_HIBERNATION_ENC_AUTH + &disk_kmk_attr.attr, +#endif &resume_offset_attr.attr, &resume_attr.attr, &image_size_attr.attr, diff --git a/kernel/power/power.h b/kernel/power/power.h index 9e58bdc8a562..fe2dfa0d4d36 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -4,6 +4,12 @@ #include #include #include +#include + +/* The max size of encrypted key blob */ +#define KEY_BLOB_BUFF_LEN 512 +#define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE +#define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE struct swsusp_info { struct new_utsname uts; @@ -20,6 +26,16 @@ struct swsusp_info { extern void __init hibernate_reserved_size_init(void); extern void __init hibernate_image_size_init(void); +#ifdef CONFIG_HIBERNATION_ENC_AUTH +/* kernel/power/snapshot_key.c */ +extern int snapshot_key_init(void); +extern bool snapshot_key_initialized(void); +extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); +extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); +#else +static inline int snapshot_key_init(void) { return 0; } +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + #ifdef CONFIG_ARCH_HIBERNATION_HEADER /* Maximum size of architecture specific data in a hibernation header */ #define MAX_ARCH_HEADER_SIZE (sizeof(struct new_utsname) + 4) diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c new file mode 100644 index 000000000000..091f33929b47 --- /dev/null +++ b/kernel/power/snapshot_key.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* snapshot keys handler + * + * Copyright (C) 2018 Lee, Chun-Yi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "power.h" + +static const char hash_alg[] = "sha512"; +static struct crypto_shash *hash_tfm; + +/* The master key of snapshot */ +static struct snapshot_key { + const char *key_name; + bool initialized; + unsigned int key_len; + u8 key[SNAPSHOT_KEY_SIZE]; +} skey = { + .key_name = "swsusp-kmk", +}; + +static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, + bool may_sleep) +{ + SHASH_DESC_ON_STACK(desc, hash_tfm); + int err; + + desc->tfm = hash_tfm; + desc->flags = may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP : 0; + + err = crypto_shash_digest(desc, buf, buflen, digest); + shash_desc_zero(desc); + return err; +} + +static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, + u8 *hash, bool may_sleep) +{ + unsigned int salted_buf_len; + u8 *salted_buf; + int ret; + + if (!key || !hash_tfm || !hash) + return -EINVAL; + + salted_buf_len = strlen(salt) + 1 + SNAPSHOT_KEY_SIZE; + salted_buf = kzalloc(salted_buf_len, + may_sleep ? GFP_KERNEL : GFP_ATOMIC); + if (!salted_buf) + return -ENOMEM; + + strcpy(salted_buf, salt); + memcpy(salted_buf + strlen(salted_buf) + 1, key, key_len); + + ret = calc_hash(hash, salted_buf, salted_buf_len, may_sleep); + memzero_explicit(salted_buf, salted_buf_len); + kzfree(salted_buf); + + return ret; +} + +/* Derive authentication/encryption key */ +static int get_derived_key(u8 *derived_key, const char *derived_type_str, + bool may_sleep) +{ + int ret; + + if (!skey.initialized || !hash_tfm) + return -EINVAL; + + ret = calc_key_hash(skey.key, skey.key_len, derived_type_str, + derived_key, may_sleep); + + return ret; +} + +int snapshot_get_auth_key(u8 *auth_key, bool may_sleep) +{ + return get_derived_key(auth_key, "AUTH_KEY", may_sleep); +} + +int snapshot_get_enc_key(u8 *enc_key, bool may_sleep) +{ + return get_derived_key(enc_key, "ENC_KEY", may_sleep); +} + +bool snapshot_key_initialized(void) +{ + return skey.initialized; +} + +static bool invalid_key(u8 *key, unsigned int key_len) +{ + int i; + + if (!key || !key_len) + return true; + + if (key_len > SNAPSHOT_KEY_SIZE) { + pr_warn("Size of swsusp key more than: %d.\n", + SNAPSHOT_KEY_SIZE); + return true; + } + + /* zero keyblob is invalid key */ + for (i = 0; i < key_len; i++) { + if (key[i] != 0) + return false; + } + pr_warn("The swsusp key should not be zero.\n"); + + return true; +} + +static int trusted_key_init(void) +{ + struct trusted_key_payload *tkp; + struct key *key; + int err; + + pr_debug("%s\n", __func__); + + /* find out swsusp-key */ + key = request_key(&key_type_trusted, skey.key_name, NULL); + if (IS_ERR(key)) { + pr_err("Request key error: %ld\n", PTR_ERR(key)); + err = PTR_ERR(key); + return err; + } + + down_write(&key->sem); + tkp = key->payload.data[0]; + if (invalid_key(tkp->key, tkp->key_len)) { + err = -EINVAL; + goto key_invalid; + } + skey.key_len = tkp->key_len; + memcpy(skey.key, tkp->key, tkp->key_len); + /* burn the original key contents */ + memzero_explicit(tkp->key, tkp->key_len); + +key_invalid: + up_write(&key->sem); + key_put(key); + + return err; +} + +static int user_key_init(void) +{ + struct user_key_payload *ukp; + struct key *key; + int err = 0; + + pr_debug("%s\n", __func__); + + /* find out swsusp-key */ + key = request_key(&key_type_user, skey.key_name, NULL); + if (IS_ERR(key)) { + pr_err("Request key error: %ld\n", PTR_ERR(key)); + err = PTR_ERR(key); + return err; + } + + down_write(&key->sem); + ukp = user_key_payload_locked(key); + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + err = -EKEYREVOKED; + goto key_invalid; + } + if (invalid_key(ukp->data, ukp->datalen)) { + err = -EINVAL; + goto key_invalid; + } + skey.key_len = ukp->datalen; + memcpy(skey.key, ukp->data, ukp->datalen); + /* burn the original key contents */ + memzero_explicit(ukp->data, ukp->datalen); + +key_invalid: + up_write(&key->sem); + key_put(key); + + return err; +} + +/* this function may sleeps */ +int snapshot_key_init(void) +{ + int err; + + pr_debug("%s\n", __func__); + + if (skey.initialized) + return 0; + + hash_tfm = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash_tfm)) { + pr_err("Can't allocate %s transform: %ld\n", + hash_alg, PTR_ERR(hash_tfm)); + return PTR_ERR(hash_tfm); + } + + err = trusted_key_init(); + if (err) + err = user_key_init(); + if (err) + goto key_fail; + + skey.initialized = true; + + pr_info("Snapshot key is initialled.\n"); + + return 0; + +key_fail: + crypto_free_shash(hash_tfm); + hash_tfm = NULL; + + return err; +} From patchwork Wed Sep 12 14:23:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chun-Yi Lee X-Patchwork-Id: 10597643 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F186D112B for ; Wed, 12 Sep 2018 14:26:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DC67C2A0DC for ; Wed, 12 Sep 2018 14:26:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CB6D929CDF; Wed, 12 Sep 2018 14:26:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 733EA29CBB for ; Wed, 12 Sep 2018 14:26:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726839AbeILTbd (ORCPT ); Wed, 12 Sep 2018 15:31:33 -0400 Received: from mail-pg1-f193.google.com ([209.85.215.193]:39147 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726792AbeILTbd (ORCPT ); Wed, 12 Sep 2018 15:31:33 -0400 Received: by mail-pg1-f193.google.com with SMTP id i190-v6so1153183pgc.6; Wed, 12 Sep 2018 07:26:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8qj2EOnN0eXjuGTJISBh6voWP92m6xtU559bfCIDDf4=; b=SDT7MglIzy1EOPjfAkkg6bfbdlieInnGOpkax1rnqL7U5NuRQFXxYHj4S594QefS/X BPoUXTrzKIfqmosMu0yDyEoSifM/QhO5DeVsG8dXTolCXnXVyGKF3yn+kqtRa2jTjj5W QkuP8k+WGsTDIImr8VDH6qHmcHOtGcJpaDvJ8BcUZFX6qy/Ls25FGRmNRPA1M7Gmqn/s 7q/5OlRjb0g6qhb2dA0oZ/o+peDpim9qNQbGVC7WLtZ2hsbEx4CqBAAzvq1UI7sXxm1a YRE9uBvDlrx7mkzn0AKQEUKJT41on12f5w1W2i5Q0rs8Ck65C1oxfHzdjpymB5SqZsCu 5AoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8qj2EOnN0eXjuGTJISBh6voWP92m6xtU559bfCIDDf4=; b=cAyicIZJ4eCkmXZBpDxkkiz+hKtlH1Q2uUjT5ECyDuIKtuBeXADp/lZ8Xojq3xtKAn fbrnihjyq1cDaDsctH8dF5T8R/7rPX98VFc7QcTuAhuLaNBf+r5twCzhrPOs/2dv9SYD s5fUZhcT7dwtw7mJlP9wcEydn8xW9pddSEu21Shyr3HnjF6wyKLrElSRtrgHmghSCqny lOZWrH/dHFmd3BV0/qkioNr/8oCNwpikaW+XTHEWWylMBsKw213ZYj9swTGstWrU9l0S BrV9O0w46GEhz/9OwocJnQVQWVpPQtwUD+5eAXW+zn3cb8QBFN5TneiST5sGC3ypqeHd 6nqA== X-Gm-Message-State: APzg51Bzrb+BkjD+q3SqFzpOIynxJH3BzhQzAMu0wOVU6IVeB1rH20aR KCau0n3l/pY95SdUOcYeJG8= X-Google-Smtp-Source: ANB0VdYIpcHYiDU1GShQP0geRnbaZ7nRiQPmxQV5C1l4shxCpFY5zpCfyj5vWDTZ6E0ix1bWpslyGw== X-Received: by 2002:a63:fe4d:: with SMTP id x13-v6mr2630507pgj.152.1536762262937; Wed, 12 Sep 2018 07:24:22 -0700 (PDT) Received: from linux-l9pv.suse ([124.11.22.254]) by smtp.gmail.com with ESMTPSA id t9-v6sm2124213pgi.87.2018.09.12.07.24.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Sep 2018 07:24:22 -0700 (PDT) From: "Lee, Chun-Yi" X-Google-Original-From: "Lee, Chun-Yi" To: "Rafael J . Wysocki" , Pavel Machek Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image Date: Wed, 12 Sep 2018 22:23:34 +0800 Message-Id: <20180912142337.21955-3-jlee@suse.com> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180912142337.21955-1-jlee@suse.com> References: <20180912142337.21955-1-jlee@suse.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When producing memory snapshot image, hibernation uses HMAC-SHA512 with snapshot key (from TPM trusted key) to calculate the hash of all data pages in snapshot image. The hash result will be kept in the snapshot header as the image signature. Before hibernation restores image, kernel executes HMAC-SHA512 again and compares the result with the signature in the header to verify the integrity of snapshot image. If the verification failed, the resume process will be stopped. Then the snapshot image will be discarded and system will boot as normal. On the other hand, a trampoline page be created in snapshot image when hibernation. This trampoline page be used to forward the state of snapshot key and the result of snapshot image verification from boot kernel to image kernel when resuming. The trampoline page will also be used to forward the snapshot key in the later patch. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 18 ++- kernel/power/power.h | 26 ++++ kernel/power/snapshot.c | 387 +++++++++++++++++++++++++++++++++++++++++++++-- kernel/power/swap.c | 6 + kernel/power/user.c | 12 ++ 5 files changed, 432 insertions(+), 17 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 18d13cbf0591..871a05e4467c 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -271,10 +271,14 @@ static int create_image(int platform_mode) { int error; + error = snapshot_prepare_hash(false); + if (error) + return error; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - return error; + goto finish_hash; } error = platform_pre_snapshot(platform_mode); @@ -331,6 +335,9 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_hash: + snapshot_finish_hash(); + return error; } @@ -694,6 +701,14 @@ int hibernate(void) return -EPERM; } + error = snapshot_key_init(); + if (error) + return error; + + error = snapshot_create_trampoline(); + if (error) + return error; + lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { @@ -750,6 +765,7 @@ int hibernate(void) pm_restore_gfp_mask(); } else { pm_pr_dbg("Image restored successfully.\n"); + snapshot_restore_trampoline(); } Free_bitmaps: diff --git a/kernel/power/power.h b/kernel/power/power.h index fe2dfa0d4d36..c614b0a294e3 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -11,6 +11,10 @@ #define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE #define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE +/* HMAC algorithm for hibernate snapshot signature */ +#define SNAPSHOT_HMAC "hmac(sha512)" +#define SNAPSHOT_DIGEST_SIZE SHA512_DIGEST_SIZE + struct swsusp_info { struct new_utsname uts; u32 version_code; @@ -19,6 +23,17 @@ struct swsusp_info { unsigned long image_pages; unsigned long pages; unsigned long size; + unsigned long trampoline_pfn; + u8 signature[SNAPSHOT_DIGEST_SIZE]; +} __aligned(PAGE_SIZE); + +/* + * The trampoline page is used to forward information + * from boot kernel to image kernel in restore stage. + */ +struct trampoline { + bool snapshot_key_valid; + int sig_verify_ret; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -27,12 +42,19 @@ extern void __init hibernate_reserved_size_init(void); extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH +/* kernel/power/snapshot.c */ +extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_hash(bool may_sleep); +extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else +static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } +static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ @@ -171,6 +193,10 @@ extern int snapshot_read_next(struct snapshot_handle *handle); extern int snapshot_write_next(struct snapshot_handle *handle); extern void snapshot_write_finalize(struct snapshot_handle *handle); extern int snapshot_image_loaded(struct snapshot_handle *handle); +extern int snapshot_create_trampoline(void); +extern void snapshot_init_trampoline(void); +extern void snapshot_restore_trampoline(void); +extern void snapshot_free_trampoline(void); /* If unset, the snapshot device cannot be open. */ extern atomic_t snapshot_device_available; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 3d37c279c090..949542ed5ffe 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -33,12 +33,16 @@ #include #include #include +#include #include #include #include #include #include +#ifdef CONFIG_HIBERNATION_ENC_AUTH +#include +#endif #include "power.h" @@ -79,6 +83,15 @@ static inline void hibernate_restore_protect_page(void *page_address) {} static inline void hibernate_restore_unprotect_page(void *page_address) {} #endif /* CONFIG_STRICT_KERNEL_RWX && CONFIG_ARCH_HAS_SET_MEMORY */ +/* the trampoline is used by image kernel */ +static void *trampoline_virt; + +/* trampoline pfn from swsusp_info in snapshot for snapshot_write_next() */ +static unsigned long trampoline_pfn; + +/* Keep the buffer for foward page in snapshot_write_next() */ +static void *trampoline_buff; + static int swsusp_page_is_free(struct page *); static void swsusp_set_page_forbidden(struct page *); static void swsusp_unset_page_forbidden(struct page *); @@ -1392,8 +1405,246 @@ static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) } #endif /* CONFIG_HIGHMEM */ -static void copy_data_pages(struct memory_bitmap *copy_bm, - struct memory_bitmap *orig_bm) +/* Total number of image pages */ +static unsigned int nr_copy_pages; + +/* Point array for collecting buffers' address in snapshot_write_next() */ +static void **h_buf; + +#ifdef CONFIG_HIBERNATION_ENC_AUTH +/* + * Signature of snapshot image + */ +static u8 signature[SNAPSHOT_DIGEST_SIZE]; + +/* Keep the signature verification result for trampoline */ +static int sig_verify_ret; + +/* keep the snapshot key status for trampoline */ +static bool snapshot_key_valid; + +static u8 *s4_verify_digest; +static struct shash_desc *s4_verify_desc; + +int snapshot_prepare_hash(bool may_sleep) +{ + char auth_key[DERIVED_KEY_SIZE]; + struct crypto_shash *tfm; + size_t digest_size, desc_size; + int ret; + + ret = snapshot_get_auth_key(auth_key, may_sleep); + if (ret) { + pr_warn_once("auth key is invalid: %d\n", ret); + return -EINVAL; + } + snapshot_key_valid = true; + + tfm = crypto_alloc_shash(SNAPSHOT_HMAC, 0, 0); + if (IS_ERR(tfm)) { + pr_err("Allocate HMAC failed: %ld\n", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + ret = crypto_shash_setkey(tfm, auth_key, DERIVED_KEY_SIZE); + if (ret) { + pr_err("Set HMAC key failed\n"); + goto error; + } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*s4_verify_desc); + digest_size = crypto_shash_digestsize(tfm); + s4_verify_digest = kzalloc(digest_size + desc_size, + may_sleep ? GFP_KERNEL : GFP_ATOMIC); + if (!s4_verify_digest) { + pr_err("Allocate digest failed\n"); + ret = -ENOMEM; + goto error; + } + + s4_verify_desc = (void *) s4_verify_digest + digest_size; + s4_verify_desc->tfm = tfm; + if (may_sleep) + s4_verify_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(s4_verify_desc); + if (ret < 0) + goto free_shash; + + return 0; + + free_shash: + kfree(s4_verify_digest); + error: + crypto_free_shash(tfm); + s4_verify_digest = NULL; + s4_verify_desc = NULL; + return ret; +} + +void snapshot_finish_hash(void) +{ + if (s4_verify_desc) + crypto_free_shash(s4_verify_desc->tfm); + kfree(s4_verify_digest); + s4_verify_desc = NULL; + s4_verify_digest = NULL; +} + +int snapshot_image_verify_decrypt(void) +{ + int ret, i; + + if (!h_buf) { + ret = -ENOMEM; + goto error; + } + + ret = snapshot_key_init(); + if (ret) + goto error_prep; + + ret = snapshot_prepare_hash(true); + if (ret || !s4_verify_desc) + goto error_prep; + + for (i = 0; i < nr_copy_pages; i++) { + ret = crypto_shash_update(s4_verify_desc, *(h_buf + i), PAGE_SIZE); + if (ret) + goto error_shash; + } + + ret = crypto_shash_final(s4_verify_desc, s4_verify_digest); + if (ret) + goto error_shash; + + pr_debug("Signature %*phN\n", SNAPSHOT_DIGEST_SIZE, signature); + pr_debug("Digest %*phN\n", SNAPSHOT_DIGEST_SIZE, s4_verify_digest); + if (memcmp(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE)) + ret = -EKEYREJECTED; + + error_shash: + snapshot_finish_hash(); + + error_prep: + vfree(h_buf); + if (ret) + pr_warn("Signature verification failed: %d\n", ret); + error: + sig_verify_ret = ret; + return ret; +} + +static int +__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) +{ + unsigned long pfn, dst_pfn; + struct page *d_page; + void *crypto_buffer = NULL; + int ret = 0; + + memory_bm_position_reset(orig_bm); + memory_bm_position_reset(copy_bm); + for (;;) { + pfn = memory_bm_next_pfn(orig_bm); + if (unlikely(pfn == BM_END_OF_MAP)) + break; + dst_pfn = memory_bm_next_pfn(copy_bm); + copy_data_page(dst_pfn, pfn); + + /* Setup buffer */ + d_page = pfn_to_page(dst_pfn); + if (PageHighMem(d_page)) { + void *kaddr = kmap_atomic(d_page); + + copy_page(buffer, kaddr); + kunmap_atomic(kaddr); + crypto_buffer = buffer; + } else { + crypto_buffer = page_address(d_page); + } + + /* Generate digest */ + if (!s4_verify_desc) + continue; + ret = crypto_shash_update(s4_verify_desc, crypto_buffer, + PAGE_SIZE); + if (ret) + return ret; + } + + if (s4_verify_desc) { + ret = crypto_shash_final(s4_verify_desc, s4_verify_digest); + if (ret) + return ret; + + memset(signature, 0, SNAPSHOT_DIGEST_SIZE); + memcpy(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE); + } + + return 0; +} + +static void alloc_h_buf(void) +{ + h_buf = vmalloc(sizeof(void *) * nr_copy_pages); + if (!h_buf) + pr_err("Allocate buffer point array failed\n"); +} + +static void init_signature(struct swsusp_info *info) +{ + memcpy(info->signature, signature, SNAPSHOT_DIGEST_SIZE); +} + +static void load_signature(struct swsusp_info *info) +{ + memset(signature, 0, SNAPSHOT_DIGEST_SIZE); + memcpy(signature, info->signature, SNAPSHOT_DIGEST_SIZE); +} + +static void init_sig_verify(struct trampoline *t) +{ + t->sig_verify_ret = sig_verify_ret; + t->snapshot_key_valid = snapshot_key_valid; + sig_verify_ret = 0; + snapshot_key_valid = 0; +} + +static void handle_sig_verify(struct trampoline *t) +{ + if (t->sig_verify_ret) + pr_warn("Signature verification failed: %d\n", + t->sig_verify_ret); + else if (t->snapshot_key_valid) + pr_info("Signature verification passed.\n"); +} +#else +static int +__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) +{ + unsigned long pfn; + + memory_bm_position_reset(orig_bm); + memory_bm_position_reset(copy_bm); + for (;;) { + pfn = memory_bm_next_pfn(orig_bm); + if (unlikely(pfn == BM_END_OF_MAP)) + break; + copy_data_page(memory_bm_next_pfn(copy_bm), pfn); + } + + return 0; +} + +static inline void alloc_h_buf(void) {} +static inline void init_signature(struct swsusp_info *info) {} +static inline void load_signature(struct swsusp_info *info) {} +static inline void init_sig_verify(struct trampoline *t) {} +static inline void handle_sig_verify(struct trampoline *t) {} +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + +static int copy_data_pages(struct memory_bitmap *copy_bm, + struct memory_bitmap *orig_bm) { struct zone *zone; unsigned long pfn; @@ -1407,18 +1658,9 @@ static void copy_data_pages(struct memory_bitmap *copy_bm, if (page_is_saveable(zone, pfn)) memory_bm_set_bit(orig_bm, pfn); } - memory_bm_position_reset(orig_bm); - memory_bm_position_reset(copy_bm); - for(;;) { - pfn = memory_bm_next_pfn(orig_bm); - if (unlikely(pfn == BM_END_OF_MAP)) - break; - copy_data_page(memory_bm_next_pfn(copy_bm), pfn); - } + return __copy_data_pages(copy_bm, orig_bm); } -/* Total number of image pages */ -static unsigned int nr_copy_pages; /* Number of pages needed for saving the original pfns of the image pages */ static unsigned int nr_meta_pages; /* @@ -1960,6 +2202,7 @@ static int swsusp_alloc(struct memory_bitmap *copy_bm, asmlinkage __visible int swsusp_save(void) { unsigned int nr_pages, nr_highmem; + int ret; pr_info("Creating hibernation image:\n"); @@ -1983,7 +2226,11 @@ asmlinkage __visible int swsusp_save(void) * Kill them. */ drain_local_pages(NULL); - copy_data_pages(©_bm, &orig_bm); + ret = copy_data_pages(©_bm, &orig_bm); + if (ret) { + pr_err("Copy data pages failed\n"); + return ret; + } /* * End of critical section. From now on, we can write to memory, @@ -2037,10 +2284,98 @@ static int init_header(struct swsusp_info *info) info->pages = snapshot_get_image_size(); info->size = info->pages; info->size <<= PAGE_SHIFT; + info->trampoline_pfn = page_to_pfn(virt_to_page(trampoline_virt)); + init_signature(info); return init_header_complete(info); } /** + * create trampoline - Create a trampoline page before snapshot be created + * In hibernation process, this routine will be called by kernel before + * the snapshot image be created. It can be used in resuming process. + */ +int snapshot_create_trampoline(void) +{ + if (trampoline_virt) { + pr_warn("Tried to create trampoline again\n"); + return 0; + } + + trampoline_virt = (void *)get_zeroed_page(GFP_KERNEL); + if (!trampoline_virt) { + pr_err("Allocate trampoline page failed\n"); + return -ENOMEM; + } + trampoline_pfn = 0; + trampoline_buff = NULL; + + return 0; +} + +/** + * initial trampoline - Put data to trampoline buffer for target kernel + * + * In resuming process, this routine will be called by boot kernel before + * the target kernel be restored. The boot kernel uses trampoline buffer + * to transfer information to target kernel. + */ +void snapshot_init_trampoline(void) +{ + struct trampoline *t; + + if (!trampoline_pfn || !trampoline_buff) { + pr_err("Did not find trampoline buffer, pfn: %ld\n", + trampoline_pfn); + return; + } + + hibernate_restore_unprotect_page(trampoline_buff); + memset(trampoline_buff, 0, PAGE_SIZE); + t = (struct trampoline *)trampoline_buff; + + init_sig_verify(t); + + pr_info("Hibernation trampoline page prepared\n"); +} + +/** + * restore trampoline - Handle the data from boot kernel and free. + * + * In resuming process, this routine will be called by target kernel + * after target kernel is restored. The target kernel handles + * the data in trampoline that it is transferred from boot kernel. + */ +void snapshot_restore_trampoline(void) +{ + struct trampoline *t; + + if (!trampoline_virt) { + pr_err("Doesn't have trampoline page\n"); + return; + } + + t = (struct trampoline *)trampoline_virt; + + handle_sig_verify(t); + snapshot_free_trampoline(); +} + +void snapshot_free_trampoline(void) +{ + if (!trampoline_virt) { + pr_err("No trampoline page can be freed\n"); + return; + } + + trampoline_pfn = 0; + trampoline_buff = NULL; + memset(trampoline_virt, 0, PAGE_SIZE); + free_page((unsigned long)trampoline_virt); + trampoline_virt = NULL; + pr_info("Trampoline freed\n"); +} + +/** * pack_pfns - Prepare PFNs for saving. * @bm: Memory bitmap. * @buf: Memory buffer to store the PFNs in. @@ -2187,6 +2522,8 @@ static int load_header(struct swsusp_info *info) if (!error) { nr_copy_pages = info->image_pages; nr_meta_pages = info->pages - info->image_pages - 1; + trampoline_pfn = info->trampoline_pfn; + load_signature(info); } return error; } @@ -2520,7 +2857,8 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm) * Get the address that snapshot_write_next() should return to its caller to * write to. */ -static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) +static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca, + unsigned long *pfn_out) { struct pbe *pbe; struct page *page; @@ -2529,6 +2867,9 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) if (pfn == BM_END_OF_MAP) return ERR_PTR(-EFAULT); + if (pfn_out) + *pfn_out = pfn; + page = pfn_to_page(pfn); if (PageHighMem(page)) return get_highmem_page_buffer(page, ca); @@ -2576,6 +2917,7 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) int snapshot_write_next(struct snapshot_handle *handle) { static struct chain_allocator ca; + unsigned long pfn; int error = 0; /* Check if we have already loaded the entire image */ @@ -2600,6 +2942,12 @@ int snapshot_write_next(struct snapshot_handle *handle) safe_pages_list = NULL; + /* Allocate buffer point array for generating + * digest to compare with signature. + * h_buf will freed in snapshot_image_verify_decrypt(). + */ + alloc_h_buf(); + error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); if (error) return error; @@ -2623,21 +2971,28 @@ int snapshot_write_next(struct snapshot_handle *handle) chain_init(&ca, GFP_ATOMIC, PG_SAFE); memory_bm_position_reset(&orig_bm); restore_pblist = NULL; - handle->buffer = get_buffer(&orig_bm, &ca); + handle->buffer = get_buffer(&orig_bm, &ca, &pfn); handle->sync_read = 0; if (IS_ERR(handle->buffer)) return PTR_ERR(handle->buffer); + if (h_buf) + *h_buf = handle->buffer; } } else { copy_last_highmem_page(); /* Restore page key for data page (s390 only). */ page_key_write(handle->buffer); hibernate_restore_protect_page(handle->buffer); - handle->buffer = get_buffer(&orig_bm, &ca); + handle->buffer = get_buffer(&orig_bm, &ca, &pfn); if (IS_ERR(handle->buffer)) return PTR_ERR(handle->buffer); if (handle->buffer != buffer) handle->sync_read = 0; + /* Capture the trampoline for transfer data */ + if (pfn == trampoline_pfn && trampoline_pfn) + trampoline_buff = handle->buffer; + if (h_buf) + *(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer; } handle->cur++; return PAGE_SIZE; diff --git a/kernel/power/swap.c b/kernel/power/swap.c index d7f6c1a288d3..2e669f589830 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -1095,6 +1095,9 @@ static int load_image(struct swap_map_handle *handle, snapshot_write_finalize(snapshot); if (!snapshot_image_loaded(snapshot)) ret = -ENODATA; + if (!ret) + ret = snapshot_image_verify_decrypt(); + snapshot_init_trampoline(); } swsusp_show_speed(start, stop, nr_to_read, "Read"); return ret; @@ -1447,6 +1450,9 @@ static int load_image_lzo(struct swap_map_handle *handle, } } } + if (!ret) + ret = snapshot_image_verify_decrypt(); + snapshot_init_trampoline(); } swsusp_show_speed(start, stop, nr_to_read, "Read"); out_clean: diff --git a/kernel/power/user.c b/kernel/power/user.c index 2d8b60a3c86b..d5c8f777e8d8 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -248,6 +248,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, if (!data->frozen || data->ready) break; pm_restore_gfp_mask(); + snapshot_restore_trampoline(); free_basic_memory_bitmaps(); data->free_bitmaps = false; thaw_processes(); @@ -259,6 +260,12 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, error = -EPERM; break; } + error = snapshot_key_init(); + if (error) + return error; + error = snapshot_create_trampoline(); + if (error) + return error; pm_restore_gfp_mask(); error = hibernation_snapshot(data->platform_support); if (!error) { @@ -275,6 +282,11 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, error = -EPERM; break; } + if (snapshot_image_verify_decrypt()) { + error = -EPERM; + break; + } + snapshot_init_trampoline(); error = hibernation_restore(data->platform_support); break; From patchwork Wed Sep 12 14:23:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chun-Yi Lee X-Patchwork-Id: 10597637 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3CFAC112B for ; Wed, 12 Sep 2018 14:24:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2B4D629C7D for ; Wed, 12 Sep 2018 14:24:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1EAEB29CBB; Wed, 12 Sep 2018 14:24:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 53FEE29C7D for ; Wed, 12 Sep 2018 14:24:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727611AbeILT3R (ORCPT ); Wed, 12 Sep 2018 15:29:17 -0400 Received: from mail-pl1-f196.google.com ([209.85.214.196]:40523 "EHLO mail-pl1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726672AbeILT3R (ORCPT ); Wed, 12 Sep 2018 15:29:17 -0400 Received: by mail-pl1-f196.google.com with SMTP id s17-v6so1058073plp.7; Wed, 12 Sep 2018 07:24:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=RaU2Lm3qPGgM5oSBiaN0Efep8ej4d8vTvibllRQBe3Q=; b=JA+262S6d+mDcnUb4Hu3hC+wNgwP4idPCuPhwr5xU+4vNlNVvpsODJO+6QJakVJow6 DtQs3aCYNNtgjXaB8yZBrSZ1rnqNc9EQvdYH9iUL0l7Oa4odDX+wT0jV2jHMppT9+S8W Booow4Cr00T3kiJXdu83/6QBCnihm8SBNeL8LMInVMVtrBozRypT7MeuEIJquQPwoMBE 4HEM42YO8CUwFQy+TTXylT/8tiPvyXqWDGzkq/LoJjXVBA3P4SBKXhY5ixd1Mo2DVIlq xAs16P66EXTmyF5TUtlu6qWwn99cAzgYO3Vm/nV9AfUGXNbs9drsp/nj7q/IoPYNAvHh Sakw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=RaU2Lm3qPGgM5oSBiaN0Efep8ej4d8vTvibllRQBe3Q=; b=aIU9lik7NxY3uCVkyPh8KFW6laWdxJjabR/wa807PP8/ffTr8SEs1pHAJ459pNExKr 8kUy/NwCA8/0rzyvQ+fJ2OP3vXHPT8rwB/naV+GdZQkmge2bAo1QdLJ4N0V/3WDah+q3 zbQvGnI77sbM5ajHQ43xN9dX8Oao4ZMCz/9LHFNyY4QaV11EC+1LC023upJts+5oD4ex ifSnSuMQLXzv5NQZzbXeT3OyPgAhYdSVdxJyaDp4wLwhy+bK1EuPDHcsvq5L0cIhTcrv p9W5d6E9bF7a3gAxi80pKs7tUqm/tPFvKKOAqcS1IjsvpvzZNzF8hZuXZ3KA4IuNFzN0 8eQg== X-Gm-Message-State: APzg51COXSQZBUryGyervYCVvaril45vFlDGEVEcaCkg8HEvG9XJN0ZZ 6ywthsFgjPYBIu/r8i/qTOo= X-Google-Smtp-Source: ANB0VdYTaSbrNANw7/w2meweRbqvY8/o/bspb4aLm6HN8xJcHcOv2Wpc0k5Ont08GBFfBlWDyQPz5Q== X-Received: by 2002:a17:902:b68d:: with SMTP id c13-v6mr2558424pls.139.1536762273058; Wed, 12 Sep 2018 07:24:33 -0700 (PDT) Received: from linux-l9pv.suse ([124.11.22.254]) by smtp.gmail.com with ESMTPSA id t9-v6sm2124213pgi.87.2018.09.12.07.24.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Sep 2018 07:24:32 -0700 (PDT) From: "Lee, Chun-Yi" X-Google-Original-From: "Lee, Chun-Yi" To: "Rafael J . Wysocki" , Pavel Machek Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: [PATCH 3/5] PM / hibernate: Encrypt snapshot image Date: Wed, 12 Sep 2018 22:23:35 +0800 Message-Id: <20180912142337.21955-4-jlee@suse.com> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180912142337.21955-1-jlee@suse.com> References: <20180912142337.21955-1-jlee@suse.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP To protect the secret in memory snapshot image, this patch adds the logic to encrypt snapshot pages by AES-CTR. Using AES-CTR is because it's simple, fast and parallelizable. But this patch didn't implement parallel encryption. The encrypt key is derived from the snapshot key. And the initialization vector will be kept in snapshot header for resuming. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 8 ++- kernel/power/power.h | 6 ++ kernel/power/snapshot.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 164 insertions(+), 4 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 871a05e4467c..79f4db284126 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -275,10 +275,14 @@ static int create_image(int platform_mode) if (error) return error; + error = snapshot_prepare_crypto(false, true); + if (error) + goto finish_hash; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - goto finish_hash; + goto finish_crypto; } error = platform_pre_snapshot(platform_mode); @@ -335,6 +339,8 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_crypto: + snapshot_finish_crypto(); finish_hash: snapshot_finish_hash(); diff --git a/kernel/power/power.h b/kernel/power/power.h index c614b0a294e3..41263fdd3a54 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -5,6 +5,7 @@ #include #include #include +#include /* The max size of encrypted key blob */ #define KEY_BLOB_BUFF_LEN 512 @@ -24,6 +25,7 @@ struct swsusp_info { unsigned long pages; unsigned long size; unsigned long trampoline_pfn; + u8 iv[AES_BLOCK_SIZE]; u8 signature[SNAPSHOT_DIGEST_SIZE]; } __aligned(PAGE_SIZE); @@ -44,6 +46,8 @@ extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH /* kernel/power/snapshot.c */ extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv); +extern void snapshot_finish_crypto(void); extern int snapshot_prepare_hash(bool may_sleep); extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ @@ -53,6 +57,8 @@ extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } +static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 949542ed5ffe..c9a6e4983571 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -41,7 +41,11 @@ #include #include #ifdef CONFIG_HIBERNATION_ENC_AUTH +#include +#include +#include #include +#include #endif #include "power.h" @@ -1412,6 +1416,127 @@ static unsigned int nr_copy_pages; static void **h_buf; #ifdef CONFIG_HIBERNATION_ENC_AUTH +static struct skcipher_request *sk_req; +static u8 iv[AES_BLOCK_SIZE]; +static void *c_buffer; + +static void init_iv(struct swsusp_info *info) +{ + memcpy(info->iv, iv, AES_BLOCK_SIZE); +} + +static void load_iv(struct swsusp_info *info) +{ + memcpy(iv, info->iv, AES_BLOCK_SIZE); +} + +int snapshot_prepare_crypto(bool may_sleep, bool create_iv) +{ + char enc_key[DERIVED_KEY_SIZE]; + struct crypto_skcipher *tfm; + int ret = 0; + + ret = snapshot_get_enc_key(enc_key, may_sleep); + if (ret) { + pr_warn_once("enc key is invalid\n"); + return -EINVAL; + } + + c_buffer = (void *)get_zeroed_page(GFP_KERNEL); + if (!c_buffer) { + pr_err("Allocate crypto buffer page failed\n"); + return -ENOMEM; + } + + tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + pr_err("failed to allocate skcipher (%d)\n", ret); + goto alloc_fail; + } + + ret = crypto_skcipher_setkey(tfm, enc_key, AES_MAX_KEY_SIZE); + if (ret) { + pr_err("failed to setkey (%d)\n", ret); + goto set_fail; + } + + sk_req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!sk_req) { + pr_err("failed to allocate request\n"); + ret = -ENOMEM; + goto set_fail; + } + if (may_sleep) + skcipher_request_set_callback(sk_req, CRYPTO_TFM_REQ_MAY_SLEEP, + NULL, NULL); + if (create_iv) + get_random_bytes(iv, AES_BLOCK_SIZE); + + return 0; + +set_fail: + crypto_free_skcipher(tfm); +alloc_fail: + __free_page(c_buffer); + + return ret; +} + +void snapshot_finish_crypto(void) +{ + struct crypto_skcipher *tfm; + + if (!sk_req) + return; + + tfm = crypto_skcipher_reqtfm(sk_req); + skcipher_request_zero(sk_req); + skcipher_request_free(sk_req); + crypto_free_skcipher(tfm); + __free_page(c_buffer); + sk_req = NULL; +} + +static int encrypt_data_page(void *hash_buffer) +{ + struct scatterlist src[1], dst[1]; + u8 iv_tmp[AES_BLOCK_SIZE]; + int ret = 0; + + if (!sk_req) + return 0; + + memcpy(iv_tmp, iv, sizeof(iv)); + sg_init_one(src, hash_buffer, PAGE_SIZE); + sg_init_one(dst, c_buffer, PAGE_SIZE); + skcipher_request_set_crypt(sk_req, src, dst, PAGE_SIZE, iv_tmp); + ret = crypto_skcipher_encrypt(sk_req); + + copy_page(hash_buffer, c_buffer); + memset(c_buffer, 0, PAGE_SIZE); + + return ret; +} + +static int decrypt_data_page(void *encrypted_page) +{ + struct scatterlist src[1], dst[1]; + u8 iv_tmp[AES_BLOCK_SIZE]; + int ret = 0; + + memcpy(iv_tmp, iv, sizeof(iv)); + sg_init_one(src, encrypted_page, PAGE_SIZE); + sg_init_one(dst, c_buffer, PAGE_SIZE); + skcipher_request_set_crypt(sk_req, src, dst, PAGE_SIZE, iv_tmp); + ret = crypto_skcipher_decrypt(sk_req); + + copy_page(encrypted_page, c_buffer); + memset(c_buffer, 0, PAGE_SIZE); + + return ret; +} + /* * Signature of snapshot image */ @@ -1507,22 +1632,30 @@ int snapshot_image_verify_decrypt(void) if (ret || !s4_verify_desc) goto error_prep; + ret = snapshot_prepare_crypto(true, false); + if (ret) + goto error_prep; + for (i = 0; i < nr_copy_pages; i++) { ret = crypto_shash_update(s4_verify_desc, *(h_buf + i), PAGE_SIZE); if (ret) - goto error_shash; + goto error_shash_crypto; + ret = decrypt_data_page(*(h_buf + i)); + if (ret) + goto error_shash_crypto; } ret = crypto_shash_final(s4_verify_desc, s4_verify_digest); if (ret) - goto error_shash; + goto error_shash_crypto; pr_debug("Signature %*phN\n", SNAPSHOT_DIGEST_SIZE, signature); pr_debug("Digest %*phN\n", SNAPSHOT_DIGEST_SIZE, s4_verify_digest); if (memcmp(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE)) ret = -EKEYREJECTED; - error_shash: + error_shash_crypto: + snapshot_finish_crypto(); snapshot_finish_hash(); error_prep: @@ -1563,6 +1696,17 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page); } + /* Encrypt hashed page */ + encrypt_data_page(crypto_buffer); + + /* Copy encrypted buffer to destination page in high memory */ + if (PageHighMem(d_page)) { + void *kaddr = kmap_atomic(d_page); + + copy_page(kaddr, crypto_buffer); + kunmap_atomic(kaddr); + } + /* Generate digest */ if (!s4_verify_desc) continue; @@ -1637,6 +1781,8 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) } static inline void alloc_h_buf(void) {} +static inline void init_iv(struct swsusp_info *info) {} +static inline void load_iv(struct swsusp_info *info) {} static inline void init_signature(struct swsusp_info *info) {} static inline void load_signature(struct swsusp_info *info) {} static inline void init_sig_verify(struct trampoline *t) {} @@ -2285,6 +2431,7 @@ static int init_header(struct swsusp_info *info) info->size = info->pages; info->size <<= PAGE_SHIFT; info->trampoline_pfn = page_to_pfn(virt_to_page(trampoline_virt)); + init_iv(info); init_signature(info); return init_header_complete(info); } @@ -2523,6 +2670,7 @@ static int load_header(struct swsusp_info *info) nr_copy_pages = info->image_pages; nr_meta_pages = info->pages - info->image_pages - 1; trampoline_pfn = info->trampoline_pfn; + load_iv(info); load_signature(info); } return error; From patchwork Wed Sep 12 14:23:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chun-Yi Lee X-Patchwork-Id: 10597639 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5474814E5 for ; Wed, 12 Sep 2018 14:24:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 43E2229C7D for ; Wed, 12 Sep 2018 14:24:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 373902A0DC; Wed, 12 Sep 2018 14:24:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8E64929C7D for ; Wed, 12 Sep 2018 14:24:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727989AbeILT3Y (ORCPT ); Wed, 12 Sep 2018 15:29:24 -0400 Received: from mail-pf1-f193.google.com ([209.85.210.193]:44516 "EHLO mail-pf1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726672AbeILT3Y (ORCPT ); Wed, 12 Sep 2018 15:29:24 -0400 Received: by mail-pf1-f193.google.com with SMTP id k21-v6so1087010pff.11; Wed, 12 Sep 2018 07:24:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=b4QbV5/izBBk04/XxTj2GLqtb8eRHVrVbQmwmvpKlrM=; b=P8I5XQ59Cta9NCY7QNhBNLZHgqCKQYOwJnYJ2huXOECExAe8HseYyXaRdww+x9s2IU FLUzuQ0d/nxj50XVHext6wFe1w2S6paaXnFKCb6ai3g0w9DPHV6lUvfMprlrJEPmjOHP yl0017jSZlRuHlSmQnoqFP3zC8mwx44EFEko7vOAs6OPpu36fO0w/eGNO/71ytoqfuri MLb3jAuOo+8/G9MsMTvTEQ4+JNDbJBC8RCIyv1ZBQJZY2XMtTMRAJvBQxTXHYehs8zCk ffzAF9lA61hy1EKG7Qy/dxMHQ1xaA6BLHoqQsqmRmWiHbzVmoH8VEg7fTtEwj7kv5JEI CrxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=b4QbV5/izBBk04/XxTj2GLqtb8eRHVrVbQmwmvpKlrM=; b=LP03HmHoBNvnL9GGCAuRi6MEZFyONXlCakK1k7ixmAtIPl6blpn58O5AqnDomQOzvT K1iY8bLgFUQmRixImszncPcszBFsvKmQTDTkwlwcbLHohL9jwGNKUfImkfVUlcwmxrAl 4ujWZYWJ78Fvs4jirDRJs28iXJq/caRlQUkD0fT4KzsdkHghPX8aoGKjFB43okopRmRp HOKodd0ykbBHcYr5AVYP71rElVUiVrH7f+Zdps0f5h7dxbsBtSkhTByBAZJeDMDupGCy 4NVJtPDOadL7RfF+9RrUNSwyZ5d53xa3yw75WSZ/A9ZNkw72ZeVkaMrOJBYKklkhCt5p M5HQ== X-Gm-Message-State: APzg51DLOe4R7k5g8KE58vBabknMlGIsWDAHw4buYxa/PkVaMzsLrotl qTBytCAq6mi6+2IFmkZAE8XcyOKR X-Google-Smtp-Source: ANB0Vdbyzd2J4pNPYcJahSBeEHXtgIXoAYe8ahTYmlP7dtgwYYHQVSuoCzLJnzr5PCYzaDCSgnzxug== X-Received: by 2002:a63:9a42:: with SMTP id e2-v6mr2678838pgo.263.1536762279995; Wed, 12 Sep 2018 07:24:39 -0700 (PDT) Received: from linux-l9pv.suse ([124.11.22.254]) by smtp.gmail.com with ESMTPSA id t9-v6sm2124213pgi.87.2018.09.12.07.24.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Sep 2018 07:24:39 -0700 (PDT) From: "Lee, Chun-Yi" X-Google-Original-From: "Lee, Chun-Yi" To: "Rafael J . Wysocki" , Pavel Machek Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: [PATCH 4/5] PM / hibernate: Erase the snapshot master key in snapshot pages Date: Wed, 12 Sep 2018 22:23:36 +0800 Message-Id: <20180912142337.21955-5-jlee@suse.com> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180912142337.21955-1-jlee@suse.com> References: <20180912142337.21955-1-jlee@suse.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP If the encryption key be guessed then the snapshot master key can also be grabbed from snapshot image. Which means that the authentication key can also be calculated. So kernel erases master key in snapshot pages. Because the master key in image kernel be erased, kernel uses the trampoline page to forward snapshot master key to image kernel. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/power.h | 6 +++++ kernel/power/snapshot.c | 5 ++++ kernel/power/snapshot_key.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/kernel/power/power.h b/kernel/power/power.h index 41263fdd3a54..d2fc73b2e200 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -36,6 +36,7 @@ struct swsusp_info { struct trampoline { bool snapshot_key_valid; int sig_verify_ret; + u8 snapshot_key[SNAPSHOT_KEY_SIZE]; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -55,6 +56,9 @@ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); +extern void snapshot_key_page_erase(unsigned long pfn, void *buff_addr); +extern void snapshot_key_trampoline_backup(struct trampoline *t); +extern void snapshot_key_trampoline_restore(struct trampoline *t); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } @@ -62,6 +66,8 @@ static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } +static inline void snapshot_key_trampoline_backup(struct trampoline *t) {} +static inline void snapshot_key_trampoline_restore(struct trampoline *t) {} #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ #ifdef CONFIG_ARCH_HIBERNATION_HEADER diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c9a6e4983571..dd176df75d2b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1696,6 +1696,9 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page); } + /* Erase key data in snapshot */ + snapshot_key_page_erase(pfn, crypto_buffer); + /* Encrypt hashed page */ encrypt_data_page(crypto_buffer); @@ -2481,6 +2484,7 @@ void snapshot_init_trampoline(void) t = (struct trampoline *)trampoline_buff; init_sig_verify(t); + snapshot_key_trampoline_backup(t); pr_info("Hibernation trampoline page prepared\n"); } @@ -2504,6 +2508,7 @@ void snapshot_restore_trampoline(void) t = (struct trampoline *)trampoline_virt; handle_sig_verify(t); + snapshot_key_trampoline_restore(t); snapshot_free_trampoline(); } diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c index 091f33929b47..c0f179283f9e 100644 --- a/kernel/power/snapshot_key.c +++ b/kernel/power/snapshot_key.c @@ -29,11 +29,26 @@ static struct snapshot_key { const char *key_name; bool initialized; unsigned int key_len; + unsigned long pfn; /* pfn of keyblob */ + unsigned long addr_offset; /* offset in page for keyblob */ u8 key[SNAPSHOT_KEY_SIZE]; + u8 fingerprint[SHA512_DIGEST_SIZE]; /* fingerprint of keyblob */ } skey = { .key_name = "swsusp-kmk", }; +static void snapshot_key_clean(void) +{ + crypto_free_shash(hash_tfm); + hash_tfm = NULL; + skey.pfn = 0; + skey.key_len = 0; + skey.addr_offset = 0; + memzero_explicit(skey.key, SNAPSHOT_KEY_SIZE); + memzero_explicit(skey.fingerprint, SHA512_DIGEST_SIZE); + skey.initialized = false; +} + static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, bool may_sleep) { @@ -74,6 +89,53 @@ static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, return ret; } +static int get_key_fingerprint(u8 *fingerprint, u8 *key, unsigned int key_len, + bool may_sleep) +{ + return calc_key_hash(key, key_len, "FINGERPRINT", fingerprint, may_sleep); +} + +void snapshot_key_page_erase(unsigned long pfn, void *buff_addr) +{ + if (!skey.initialized || pfn != skey.pfn) + return; + + /* erase key data from snapshot buffer page */ + if (!memcmp(skey.key, buff_addr + skey.addr_offset, skey.key_len)) { + memzero_explicit(buff_addr + skey.addr_offset, skey.key_len); + pr_info("Erased swsusp key in snapshot pages.\n"); + } +} + +/* this function may sleeps because snapshot_key_init() */ +void snapshot_key_trampoline_backup(struct trampoline *t) +{ + if (!t || snapshot_key_init()) + return; + + memcpy(t->snapshot_key, skey.key, skey.key_len); +} + +/* Be called after snapshot image restored success */ +void snapshot_key_trampoline_restore(struct trampoline *t) +{ + u8 fingerprint[SHA512_DIGEST_SIZE]; + + if (!skey.initialized || !t) + return; + + /* check key fingerprint before restore */ + get_key_fingerprint(fingerprint, t->snapshot_key, skey.key_len, true); + if (memcmp(skey.fingerprint, fingerprint, SHA512_DIGEST_SIZE)) { + pr_warn("Restored swsusp key failed, fingerprint mismatch.\n"); + snapshot_key_clean(); + return; + } + + memcpy(skey.key, t->snapshot_key, skey.key_len); + memzero_explicit(t->snapshot_key, SNAPSHOT_KEY_SIZE); +} + /* Derive authentication/encryption key */ static int get_derived_key(u8 *derived_key, const char *derived_type_str, bool may_sleep) @@ -223,9 +285,13 @@ int snapshot_key_init(void) if (err) goto key_fail; + skey.pfn = page_to_pfn(virt_to_page(skey.key)); + skey.addr_offset = (unsigned long) skey.key & ~PAGE_MASK; + get_key_fingerprint(skey.fingerprint, skey.key, skey.key_len, true); skey.initialized = true; pr_info("Snapshot key is initialled.\n"); + pr_debug("Fingerprint %*phN\n", SHA512_DIGEST_SIZE, skey.fingerprint); return 0; From patchwork Wed Sep 12 14:23:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chun-Yi Lee X-Patchwork-Id: 10597641 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8ABC214E5 for ; Wed, 12 Sep 2018 14:25:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 79A2029C7D for ; Wed, 12 Sep 2018 14:25:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6CF1A29CB1; Wed, 12 Sep 2018 14:25:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A80E829CBB for ; Wed, 12 Sep 2018 14:25:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726672AbeILT3x (ORCPT ); Wed, 12 Sep 2018 15:29:53 -0400 Received: from mail-pf1-f195.google.com ([209.85.210.195]:44564 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726537AbeILT3x (ORCPT ); Wed, 12 Sep 2018 15:29:53 -0400 Received: by mail-pf1-f195.google.com with SMTP id k21-v6so1087666pff.11; Wed, 12 Sep 2018 07:25:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mIYZkmlAKW9DXMB6mufGujz5hO7WeUIoyhtH0q4lSLQ=; b=YPvmzxBJhYf5HKpBTs729PIKgHbXuY/xWQKCme8njWS96JEpC3gPuIhbj9XPGhtCai W0VCj0Nm9qaQxiuUhrGdTZpeqb2UFDkGry4YFZ0v2EKgONm6DaxhQaqb+EBIMa/xP0qd UAS7Dzz49I9ZnbRtBzDagzTG5eqyv7htmzoIf6kJTN6PNBk+K7G2ZFFULUzb8OFwLhXI UQBnDZP4/QQsGk18tV3r1km5GJ4kWwHRtZmwJkST3mwV5GwiFOt3CZrOTHKBX3fzzbX4 OR3L9D6XDbVDAVG3kenJ7Tq4c8L6n05QV9zkSAPUSaSdGJG0N1qofzMPAP/CkDloMVCr wPjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mIYZkmlAKW9DXMB6mufGujz5hO7WeUIoyhtH0q4lSLQ=; b=qi6oqJeVpOL9ZbHTU3zOt+Tq3thYJ2Pstf7hwZuWJvJ51Qc/CLXTAkJBTpuspRI2gT bcwv/b4cU9qk2SNEme8MFySLhLhNX4FV9dO78Qk7DgSh/i++TuROiIhAD8651AJvt/Nt 2aO371TUOimxoka7Dt8t0qVEWnYgUw9y1C8itrmdSWM5Cg0PRtyWdXzj1Rjsg+8go8U+ qAL022B6ZgnKs8evse1wUnruog9L8u+jitegNZhIIeWWepLp7C6bidCqqetJp2rTUOD/ ctdIhuHMeaFT6owj2MkXYHiuIxWuze5IXp47sRPJ1Yba67yBMykYfxZJ9FNLcyB5So2R 6+CA== X-Gm-Message-State: APzg51Cy+wJNYfP7SGLDC70OfIfaantLJQAP2sSW5DL+xi/KEaqqx1dV sXzY0XddkVs1jks08tHz/Dw= X-Google-Smtp-Source: ANB0VdYKzuD1pUQJl60qjuklRRBFd35JE2nsu/FxuX2LOgZvn0iSQj7CyujToVoSMVbWj5XSTlITbQ== X-Received: by 2002:a63:d401:: with SMTP id a1-v6mr2624849pgh.414.1536762308735; Wed, 12 Sep 2018 07:25:08 -0700 (PDT) Received: from linux-l9pv.suse ([124.11.22.254]) by smtp.gmail.com with ESMTPSA id t9-v6sm2124213pgi.87.2018.09.12.07.24.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Sep 2018 07:25:08 -0700 (PDT) From: "Lee, Chun-Yi" X-Google-Original-From: "Lee, Chun-Yi" To: "Rafael J . Wysocki" , Pavel Machek Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Lee, Chun-Yi" , "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich Subject: [PATCH 5/5] PM / hibernate: An option to request that snapshot image must be authenticated Date: Wed, 12 Sep 2018 22:23:37 +0800 Message-Id: <20180912142337.21955-6-jlee@suse.com> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180912142337.21955-1-jlee@suse.com> References: <20180912142337.21955-1-jlee@suse.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This kernel option is similar to the option for kernel module signature verification. When this option is unselected, kernel will be tainted by restored from a snapshot image without (valid) signature. When the option is selected, kernel will refuse the system to be restored from a unauthenticated image. The hibernation resume process will be stopped , the snapshot image will be discarded and system just boots as normal. The hibernation can be triggered without snapshot master key when this option is unselected. But kernel will be tainted after hibernation resume. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/kernel-parameters.txt | 6 ++++ include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig | 11 +++++++ kernel/power/hibernate.c | 8 +++-- kernel/power/power.h | 5 ++++ kernel/power/snapshot.c | 40 +++++++++++++++++++++++-- kernel/power/user.c | 2 +- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 970d837bd57f..c4be29103865 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3941,6 +3941,12 @@ protect_image Turn on image protection during restoration (that will set all pages holding image data during restoration read-only). + enforce_auth When HIBERNATION_ENC_AUTH is set, this means + that snapshot image without (valid) signature + will fail to restore. + Note that if HIBERNATION_ENC_AUTH_FORCE is + set, that is always true, so this option does + nothing. retain_initrd [RAM] Keep initrd memory after extraction diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d6aac75b51ba..61714489cb57 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -598,7 +598,8 @@ extern enum system_states { #define TAINT_LIVEPATCH 15 #define TAINT_AUX 16 #define TAINT_RANDSTRUCT 17 -#define TAINT_FLAGS_COUNT 18 +#define TAINT_UNSAFE_HIBERNATE 18 +#define TAINT_FLAGS_COUNT 19 struct taint_flag { char c_true; /* character printed when tainted */ diff --git a/kernel/panic.c b/kernel/panic.c index 8b2e002d52eb..624eb1150361 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -327,6 +327,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { [ TAINT_LIVEPATCH ] = { 'K', ' ', true }, [ TAINT_AUX ] = { 'X', ' ', true }, [ TAINT_RANDSTRUCT ] = { 'T', ' ', true }, + [ TAINT_UNSAFE_HIBERNATE ] = { 'H', ' ', true }, }; /** diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 7c5c30149dbc..3c998fd6dc4c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -90,6 +90,17 @@ config HIBERNATION_ENC_AUTH master key of hibernation. The TPM trusted key depends on TPM. The security of user defined key relies on user space. +config HIBERNATION_ENC_AUTH_FORCE + bool "Require hibernate snapshot image to be validly signed" + depends on HIBERNATION_ENC_AUTH + help + This option will prevent that a snapshot image without (valid) + signature be restored. Without this option, a unauthenticated + snapshot image can be restored but the restored kernel will be + tainted. Which also means that the hibernation can be triggered + without snapshot key but kernel will be tainted without this + option. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 79f4db284126..70a0d630a6c2 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -272,11 +272,11 @@ static int create_image(int platform_mode) int error; error = snapshot_prepare_hash(false); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_prepare_crypto(false, true); - if (error) + if (error && snapshot_is_enforce_auth()) goto finish_hash; error = dpm_suspend_end(PMSG_FREEZE); @@ -708,7 +708,7 @@ int hibernate(void) } error = snapshot_key_init(); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_create_trampoline(); @@ -1251,6 +1251,8 @@ static int __init hibernate_setup(char *str) } else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && !strncmp(str, "protect_image", 13)) { enable_restore_image_protection(); + } else if (!strncmp(str, "enforce_auth", 10)) { + snapshot_set_enforce_auth(); } return 1; } diff --git a/kernel/power/power.h b/kernel/power/power.h index d2fc73b2e200..edb63991bcdc 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -36,6 +36,7 @@ struct swsusp_info { struct trampoline { bool snapshot_key_valid; int sig_verify_ret; + bool enforce_auth; u8 snapshot_key[SNAPSHOT_KEY_SIZE]; } __aligned(PAGE_SIZE); @@ -51,6 +52,8 @@ extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv); extern void snapshot_finish_crypto(void); extern int snapshot_prepare_hash(bool may_sleep); extern void snapshot_finish_hash(void); +extern void snapshot_set_enforce_auth(void); +extern int snapshot_is_enforce_auth(void); /* kernel/power/snapshot_key.c */ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); @@ -65,6 +68,8 @@ static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { retu static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} +static inline void snapshot_set_enforce_auth(void) {} +static inline int snapshot_is_enforce_auth(void) { return 0; } static inline int snapshot_key_init(void) { return 0; } static inline void snapshot_key_trampoline_backup(struct trampoline *t) {} static inline void snapshot_key_trampoline_restore(struct trampoline *t) {} diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index dd176df75d2b..803340dfe8ef 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1537,6 +1537,23 @@ static int decrypt_data_page(void *encrypted_page) return ret; } +/* enforce the snapshot to be signed */ +#ifdef CONFIG_HIBERNATION_ENC_AUTH_FORCE +static bool enforce_auth = true; +#else +static bool enforce_auth; +#endif + +void snapshot_set_enforce_auth(void) +{ + enforce_auth = true; +} + +int snapshot_is_enforce_auth(void) +{ + return enforce_auth; +} + /* * Signature of snapshot image */ @@ -1603,6 +1620,8 @@ int snapshot_prepare_hash(bool may_sleep) crypto_free_shash(tfm); s4_verify_digest = NULL; s4_verify_desc = NULL; + if (!enforce_auth) + ret = 0; return ret; } @@ -1664,6 +1683,8 @@ int snapshot_image_verify_decrypt(void) pr_warn("Signature verification failed: %d\n", ret); error: sig_verify_ret = ret; + if (!enforce_auth) + ret = 0; return ret; } @@ -1751,6 +1772,7 @@ static void load_signature(struct swsusp_info *info) static void init_sig_verify(struct trampoline *t) { + t->enforce_auth = enforce_auth; t->sig_verify_ret = sig_verify_ret; t->snapshot_key_valid = snapshot_key_valid; sig_verify_ret = 0; @@ -1759,11 +1781,25 @@ static void init_sig_verify(struct trampoline *t) static void handle_sig_verify(struct trampoline *t) { - if (t->sig_verify_ret) + enforce_auth = t->enforce_auth; + if (enforce_auth) + pr_info("Enforce the snapshot to be validly signed\n"); + + if (t->sig_verify_ret) { pr_warn("Signature verification failed: %d\n", t->sig_verify_ret); - else if (t->snapshot_key_valid) + if (t->snapshot_key_valid) + pr_warn("Did not find valid snapshot key.\n"); + /* taint kernel */ + if (!enforce_auth) { + pr_warn("System resumed from unsafe snapshot - " + "tainting kernel\n"); + add_taint(TAINT_UNSAFE_HIBERNATE, LOCKDEP_STILL_OK); + pr_info("%s\n", print_tainted()); + } + } else if (t->snapshot_key_valid) { pr_info("Signature verification passed.\n"); + } } #else static int diff --git a/kernel/power/user.c b/kernel/power/user.c index d5c8f777e8d8..9597f48f01d0 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -261,7 +261,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, break; } error = snapshot_key_init(); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_create_trampoline(); if (error)