From patchwork Mon Oct 23 21:40:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829649 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="glzQYSNs"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHL20dwz9sPm for ; Tue, 24 Oct 2017 08:42:22 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932101AbdJWVmR (ORCPT ); Mon, 23 Oct 2017 17:42:17 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:45644 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751282AbdJWVmL (ORCPT ); Mon, 23 Oct 2017 17:42:11 -0400 Received: by mail-io0-f196.google.com with SMTP id i38so21712900iod.2; Mon, 23 Oct 2017 14:42:11 -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=RWzr6AEc1Y+08Sc6Z5TXghPCExRxgnlcGUvUgvGnKio=; b=glzQYSNsSBDLDE/nk4frqeg3HNva3bS7lXX45otUIZry+TOorx2ur4YhytLLdHBboa nJYL8OmAh+KT1AQsBTAT0fm8lCRzjhZF2x9JMNSiF0L8qXHirwRaFbyqAfX6NfP0ojSr i3Iv6kdyHr3Mefd8shSvkh/OQUdlZeXsGUElN5vr6CgmMk3vgpTsZs4djk1VxxORTn/j h/Hs07C+BkkGu3SuSJlcBJyb6ltNjLSveyGHUHiRBkvJ8jtiI1LFPvcrpOzspGUW9AIU Sa/fy7U9zAzOYj+MazmfPukQbaclsIAGVWqvEa8B4+6CJzUjrhVvvk3vzDl9TQtrnbCd 4E+w== 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=RWzr6AEc1Y+08Sc6Z5TXghPCExRxgnlcGUvUgvGnKio=; b=ennJgrDe2qcQAiGu2YQnSHCReBbmlzFd5OhS1nU/LD49wCSwryMS2GLSFGYJ4oteAw kC48qOFNH90AsRbK4p9kffMRdJDrY/mh0ZbuIN6T8yqNPJlA9GQRaGxqac/AyLBtwddQ PZTq5y3POKR05aBNBQiHG4A3GSBJnoKmhsOu0AFsYQB5WBCeyPBnco2iydp+AkPOyD+M c02EhHEHPt7sY1SQ8/mK2Nq4nScwKFz5MekX+H7bgKrlCrXyspJDrV1o+pTjDh748taB iqhHVQ/enNZZYy17qG5JhtOagnLQ1/AlVx/i48X3jeFcjOl7JRikPvcbL2F+3l7EfLD2 PYjQ== X-Gm-Message-State: AMCzsaXI2vAfFWKUG8TsYmGbVgkHJDRhT3BG8B91LOMk6blBq7obOcjn moL66LGC04fYU08lsq86hIEXkZvf X-Google-Smtp-Source: ABhQp+SkwdGNYgYLIZc75kQ10EaSGMzOQA0keAn+yXuFTLWSYti8dpoKFAj2BVpivHhmCf6mtFoV5w== X-Received: by 10.107.29.73 with SMTP id d70mr18396373iod.201.1508794930798; Mon, 23 Oct 2017 14:42:10 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:10 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 01/25] fs, fscrypt: move uapi definitions to new header Date: Mon, 23 Oct 2017 14:40:34 -0700 Message-Id: <20171023214058.128121-2-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers There are going to be more filesystem encryption definitions added, and we don't want to use a disproportionate amount of space in for filesystem encryption stuff. So move the fscrypt definitions to a new header . For compatibility with existing userspace programs which may be including , still includes the new header. (It's debatable whether we really need this, though; the filesystem encryption API is new enough that most if not all programs that are using it have to declare it themselves anyway.) Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- include/linux/fscrypt.h | 2 +- include/uapi/linux/fs.h | 50 +++-------------------------------------- include/uapi/linux/fscrypt.h | 53 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 include/uapi/linux/fscrypt.h diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 53437bfdfcbc..f7aa7d62e235 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #define FS_CRYPTO_BLOCK_SIZE 16 diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 56235dddea7d..6ecd3ee9960c 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -12,6 +12,9 @@ #include #include #include +#ifndef __KERNEL__ +#include +#endif /* * It's silly to have NR_OPEN bigger than NR_FILE, but you can change @@ -253,53 +256,6 @@ struct fsxattr { #define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) #define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) -/* - * File system encryption support - */ -/* Policy provided via an ioctl on the topmost directory */ -#define FS_KEY_DESCRIPTOR_SIZE 8 - -#define FS_POLICY_FLAGS_PAD_4 0x00 -#define FS_POLICY_FLAGS_PAD_8 0x01 -#define FS_POLICY_FLAGS_PAD_16 0x02 -#define FS_POLICY_FLAGS_PAD_32 0x03 -#define FS_POLICY_FLAGS_PAD_MASK 0x03 -#define FS_POLICY_FLAGS_VALID 0x03 - -/* Encryption algorithms */ -#define FS_ENCRYPTION_MODE_INVALID 0 -#define FS_ENCRYPTION_MODE_AES_256_XTS 1 -#define FS_ENCRYPTION_MODE_AES_256_GCM 2 -#define FS_ENCRYPTION_MODE_AES_256_CBC 3 -#define FS_ENCRYPTION_MODE_AES_256_CTS 4 -#define FS_ENCRYPTION_MODE_AES_128_CBC 5 -#define FS_ENCRYPTION_MODE_AES_128_CTS 6 - -struct fscrypt_policy { - __u8 version; - __u8 contents_encryption_mode; - __u8 filenames_encryption_mode; - __u8 flags; - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; -}; - -#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) -#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) -#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) - -/* Parameters for passing an encryption key into the kernel keyring */ -#define FS_KEY_DESC_PREFIX "fscrypt:" -#define FS_KEY_DESC_PREFIX_SIZE 8 - -/* Structure that userspace passes to the kernel keyring */ -#define FS_MAX_KEY_SIZE 64 - -struct fscrypt_key { - __u32 mode; - __u8 raw[FS_MAX_KEY_SIZE]; - __u32 size; -}; - /* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) * diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h new file mode 100644 index 000000000000..c09209fc42ea --- /dev/null +++ b/include/uapi/linux/fscrypt.h @@ -0,0 +1,53 @@ +#ifndef _UAPI_LINUX_FSCRYPT_H +#define _UAPI_LINUX_FSCRYPT_H + +#include + +/* + * File system encryption support + */ +/* Policy provided via an ioctl on the topmost directory */ +#define FS_KEY_DESCRIPTOR_SIZE 8 + +#define FS_POLICY_FLAGS_PAD_4 0x00 +#define FS_POLICY_FLAGS_PAD_8 0x01 +#define FS_POLICY_FLAGS_PAD_16 0x02 +#define FS_POLICY_FLAGS_PAD_32 0x03 +#define FS_POLICY_FLAGS_PAD_MASK 0x03 +#define FS_POLICY_FLAGS_VALID 0x03 + +/* Encryption algorithms */ +#define FS_ENCRYPTION_MODE_INVALID 0 +#define FS_ENCRYPTION_MODE_AES_256_XTS 1 +#define FS_ENCRYPTION_MODE_AES_256_GCM 2 +#define FS_ENCRYPTION_MODE_AES_256_CBC 3 +#define FS_ENCRYPTION_MODE_AES_256_CTS 4 +#define FS_ENCRYPTION_MODE_AES_128_CBC 5 +#define FS_ENCRYPTION_MODE_AES_128_CTS 6 + +struct fscrypt_policy { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; +}; + +#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) +#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) + +/* Parameters for passing an encryption key into the kernel keyring */ +#define FS_KEY_DESC_PREFIX "fscrypt:" +#define FS_KEY_DESC_PREFIX_SIZE 8 + +/* Structure that userspace passes to the kernel keyring */ +#define FS_MAX_KEY_SIZE 64 + +struct fscrypt_key { + __u32 mode; + __u8 raw[FS_MAX_KEY_SIZE]; + __u32 size; +}; + +#endif /* _UAPI_LINUX_FSCRYPT_H */ From patchwork Mon Oct 23 21:40:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829673 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="uSklb3u6"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKL30wMz9sPr for ; Tue, 24 Oct 2017 08:44:06 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751665AbdJWVmW (ORCPT ); Mon, 23 Oct 2017 17:42:22 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:56920 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751524AbdJWVmO (ORCPT ); Mon, 23 Oct 2017 17:42:14 -0400 Received: by mail-io0-f193.google.com with SMTP id m81so21685407ioi.13; Mon, 23 Oct 2017 14:42:12 -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=loucawc3/CQiBAXgvcIlW3xlYUgnNzHJZkRDJfZAax8=; b=uSklb3u6CI9TowIdZ0ktsKRv2Ji1MJRLDr8NRI7tkfUdod2njNIp1VJL2eYfLg12e6 4KLFOvwa1cqg9xdkS3AT97rz7GK+zE1/Vq1Yz+oI5YEpYY6fLt9x74qdVj2dGs7/19qj nKwIg9fiZ6aI8BIbZPanzxfAPu+e/UTEXJDk8LcIcV0/pUni4LKr831blr9dI1omhZUc Eg34g9QwYhhHnha/HW3Qm4g+7wxkhXn4H6cIRMSeAJEvfwNgxIhqDtyq+D6jdMhDMB9Y TWyjH19huL1bn4B9g35WEECuqiIXICNTvzctwtYXh1DaC+eCPnahZQNE14FJsvs9udf8 WHwg== 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=loucawc3/CQiBAXgvcIlW3xlYUgnNzHJZkRDJfZAax8=; b=qwcrh//cZh67vDw9jPzMBuZoa5njBJU/Gg27THLSNzoPmZgcO3echmj708dWJ/XHdF GkZPp6v5IMsD8DhtZVHGJWHNr3oEnTeMqGTLoqtrFHe3ozqmjA1Owhmt91ewoFGvES+p qAF3d5WbwI+YytxtYiYVkWT9FQmr+21j+Z/0qZ5LGC6bVScioczUYZKVczvDnM9jW5J7 F5Y68td3V34OelvOUanftJ5WFrVlUtoCesGI1cYctTHPe5mrxyk1OlZi8nKtRQ9aIgaI zy3bFEWES9QkN72/qq2G/hbh9OKhUrKv2XdzEigxPz3E5tQSx2IMwUJeoxXo2KrHtk+D jdUQ== X-Gm-Message-State: AMCzsaWx+MfgMS12j1jqP5SwY88dM60dpOBc5t2clZzJx7wMW9OKZc5h r3j138UTgIgbV4hmTfQhCKcRZUkB X-Google-Smtp-Source: ABhQp+Re5rCHRPrbMbuzWjIj9cd68i0Hd2eWi0ZCQU+UqIW/7fIk/AOCECgu3focwICn0tcDvySzEQ== X-Received: by 10.107.162.67 with SMTP id l64mr19223690ioe.278.1508794932077; Mon, 23 Oct 2017 14:42:12 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.10 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:11 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 02/25] fscrypt: use FSCRYPT_ prefix for uapi constants Date: Mon, 23 Oct 2017 14:40:35 -0700 Message-Id: <20171023214058.128121-3-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Prefix all filesystem encryption UAPI constants except the ioctl numbers with "FSCRYPT_" rather than with "FS_". This namespaces the constants more appropriately and makes it clear that they are related specifically to the filesystem encryption feature, and to the 'fscrypt_*' structures. With some of the old names like "FS_POLICY_FLAGS_VALID", it was not immediately clear that the constant had anything to do with encryption. This is also useful because we'll be adding more encryption-related constants, e.g. for the policy version, and we'd otherwise have to choose whether to use unclear names like FS_POLICY_VERSION_* or inconsistent names like FS_ENCRYPTION_POLICY_VERSION_*. For source compatibility with older userspace programs, keep the old names defined as aliases to the new ones. (It's debatable whether we really need this, though; the filesystem encryption API is new enough that most if not all programs that are using it have to declare it themselves anyway.) Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- Documentation/filesystems/fscrypt.rst | 14 ++++----- include/uapi/linux/fscrypt.h | 59 ++++++++++++++++++++++++----------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 776ddc655f79..f12956f3f1f8 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -251,14 +251,14 @@ empty directory or verifies that a directory or regular file already has the specified encryption policy. It takes in a pointer to a :c:type:`struct fscrypt_policy`, defined as follows:: - #define FS_KEY_DESCRIPTOR_SIZE 8 + #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 struct fscrypt_policy { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; This structure must be initialized as follows: @@ -274,7 +274,7 @@ This structure must be initialized as follows: - ``flags`` must be set to a value from ```` which identifies the amount of NUL-padding to use when encrypting - filenames. If unsure, use FS_POLICY_FLAGS_PAD_32 (0x3). + filenames. If unsure, use FSCRYPT_POLICY_FLAGS_PAD_32 (0x3). - ``master_key_descriptor`` specifies how to find the master key in the keyring; see `Adding keys`_. It is up to userspace to choose a @@ -374,11 +374,11 @@ followed by the 16-character lower case hex representation of the ``master_key_descriptor`` that was set in the encryption policy. The key payload must conform to the following structure:: - #define FS_MAX_KEY_SIZE 64 + #define FSCRYPT_MAX_KEY_SIZE 64 struct fscrypt_key { u32 mode; - u8 raw[FS_MAX_KEY_SIZE]; + u8 raw[FSCRYPT_MAX_KEY_SIZE]; u32 size; }; @@ -533,7 +533,7 @@ much confusion if an encryption policy were to be added to or removed from anything other than an empty directory.) The struct is defined as follows:: - #define FS_KEY_DESCRIPTOR_SIZE 8 + #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 #define FS_KEY_DERIVATION_NONCE_SIZE 16 struct fscrypt_context { @@ -541,7 +541,7 @@ as follows:: u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; - u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; }; diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index c09209fc42ea..26c381a40279 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -7,30 +7,31 @@ * File system encryption support */ /* Policy provided via an ioctl on the topmost directory */ -#define FS_KEY_DESCRIPTOR_SIZE 8 +#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 -#define FS_POLICY_FLAGS_PAD_4 0x00 -#define FS_POLICY_FLAGS_PAD_8 0x01 -#define FS_POLICY_FLAGS_PAD_16 0x02 -#define FS_POLICY_FLAGS_PAD_32 0x03 -#define FS_POLICY_FLAGS_PAD_MASK 0x03 -#define FS_POLICY_FLAGS_VALID 0x03 +/* Encryption policy flags */ +#define FSCRYPT_POLICY_FLAGS_PAD_4 0x00 +#define FSCRYPT_POLICY_FLAGS_PAD_8 0x01 +#define FSCRYPT_POLICY_FLAGS_PAD_16 0x02 +#define FSCRYPT_POLICY_FLAGS_PAD_32 0x03 +#define FSCRYPT_POLICY_FLAGS_PAD_MASK 0x03 +#define FSCRYPT_POLICY_FLAGS_VALID 0x03 /* Encryption algorithms */ -#define FS_ENCRYPTION_MODE_INVALID 0 -#define FS_ENCRYPTION_MODE_AES_256_XTS 1 -#define FS_ENCRYPTION_MODE_AES_256_GCM 2 -#define FS_ENCRYPTION_MODE_AES_256_CBC 3 -#define FS_ENCRYPTION_MODE_AES_256_CTS 4 -#define FS_ENCRYPTION_MODE_AES_128_CBC 5 -#define FS_ENCRYPTION_MODE_AES_128_CTS 6 +#define FSCRYPT_MODE_INVALID 0 +#define FSCRYPT_MODE_AES_256_XTS 1 +#define FSCRYPT_MODE_AES_256_GCM 2 +#define FSCRYPT_MODE_AES_256_CBC 3 +#define FSCRYPT_MODE_AES_256_CTS 4 +#define FSCRYPT_MODE_AES_128_CBC 5 +#define FSCRYPT_MODE_AES_128_CTS 6 struct fscrypt_policy { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; #define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) @@ -38,16 +39,36 @@ struct fscrypt_policy { #define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) /* Parameters for passing an encryption key into the kernel keyring */ -#define FS_KEY_DESC_PREFIX "fscrypt:" -#define FS_KEY_DESC_PREFIX_SIZE 8 +#define FSCRYPT_KEY_DESC_PREFIX "fscrypt:" +#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8 /* Structure that userspace passes to the kernel keyring */ -#define FS_MAX_KEY_SIZE 64 +#define FSCRYPT_MAX_KEY_SIZE 64 struct fscrypt_key { __u32 mode; - __u8 raw[FS_MAX_KEY_SIZE]; + __u8 raw[FSCRYPT_MAX_KEY_SIZE]; __u32 size; }; +/**********************************************************************/ + +/* old names; don't add anything new here! */ +#define FS_POLICY_FLAGS_PAD_4 FSCRYPT_POLICY_FLAGS_PAD_4 +#define FS_POLICY_FLAGS_PAD_8 FSCRYPT_POLICY_FLAGS_PAD_8 +#define FS_POLICY_FLAGS_PAD_16 FSCRYPT_POLICY_FLAGS_PAD_16 +#define FS_POLICY_FLAGS_PAD_32 FSCRYPT_POLICY_FLAGS_PAD_32 +#define FS_POLICY_FLAGS_PAD_MASK FSCRYPT_POLICY_FLAGS_PAD_MASK +#define FS_POLICY_FLAGS_VALID FSCRYPT_POLICY_FLAGS_VALID +#define FS_KEY_DESCRIPTOR_SIZE FSCRYPT_KEY_DESCRIPTOR_SIZE +#define FS_ENCRYPTION_MODE_INVALID FSCRYPT_MODE_INVALID +#define FS_ENCRYPTION_MODE_AES_256_XTS FSCRYPT_MODE_AES_256_XTS +#define FS_ENCRYPTION_MODE_AES_256_GCM FSCRYPT_MODE_AES_256_GCM +#define FS_ENCRYPTION_MODE_AES_256_CBC FSCRYPT_MODE_AES_256_CBC +#define FS_ENCRYPTION_MODE_AES_256_CTS FSCRYPT_MODE_AES_256_CTS +#define FS_ENCRYPTION_MODE_AES_128_CBC FSCRYPT_MODE_AES_128_CBC +#define FS_ENCRYPTION_MODE_AES_128_CTS FSCRYPT_MODE_AES_128_CTS +#define FS_KEY_DESC_PREFIX FSCRYPT_KEY_DESC_PREFIX +#define FS_KEY_DESC_PREFIX_SIZE FSCRYPT_KEY_DESC_PREFIX_SIZE +#define FS_MAX_KEY_SIZE FSCRYPT_MAX_KEY_SIZE #endif /* _UAPI_LINUX_FSCRYPT_H */ From patchwork Mon Oct 23 21:40:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829650 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Z5VF66EQ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHS3GTZz9sPm for ; Tue, 24 Oct 2017 08:42:28 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932175AbdJWVm0 (ORCPT ); Mon, 23 Oct 2017 17:42:26 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:55139 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751432AbdJWVmO (ORCPT ); Mon, 23 Oct 2017 17:42:14 -0400 Received: by mail-io0-f196.google.com with SMTP id e89so21711925ioi.11; Mon, 23 Oct 2017 14:42:14 -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=UIdBN3CE/fFP8k/jp0riCfEZRfy+HENluYQKnQvJikI=; b=Z5VF66EQ/+TpkjBkAq5HetJTRMqMGRoZsthoenKnv8SUDbRGGI7rAhNCFrI64Bc4CP v1Lbi5iB8dJf3zQmKZhw1JeECFHHam4wpGrEyNvwmbcIdBhPHyFjltgysjt1Dukq9fq1 ApJuQbUCjPagh6Pt8QWDeVvnSlGYhIBhPEcYv29MGfExH47JNXYHSrgcXiiiwDX79Odq jKcu2wyX3LHJPPPY8o4ikxVhd+Z5WJUi03BzkEnPWXC3CXI+J/4IzWIgtoIV2MoDpumj onV2NMdfbG46nlOukMXh11m1G25bZUre6HdH6Fwm9quLwar/Fx9pWoyWaSLA89sIENw3 wPpw== 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=UIdBN3CE/fFP8k/jp0riCfEZRfy+HENluYQKnQvJikI=; b=ghoFHLb7tPGPfX+DLxj8hwG551XvH/m55QcPvMNWISJtNVWbpXcMvTYU+i4rljkNMa fd8pm6B+JIV5QQ/eMUDxl9rEgQtWqp4iuZ8t283N4DKx/8CvgCoao94k9t/JZ4Ujw/C/ O6+2VLZzgyAUpzHalM1Uw5SQ5aW0DpcQa+UlH2U299ljOUvTpurjMSVcRY3+CDRp4xJw s7ISPZZhe0PVp4F0Xkt0KkXJz+oe93jnOIIICO3OKED1xo67vk76mR5O121vJcv8WKEs PFjZcko4f9Kxqw/d4f5hV3l9qHHrOLXY9rhJtWhABvZWSYovr7GRuNUGtfGymz+45BaY /VaA== X-Gm-Message-State: AMCzsaXHSSL+txfvrpmk4i4+WUhbv7nAV7xljnfHIKq5pWCt9c+Ct8dh DtnHObAU45pkuUw9XxiTKSajSbPJ X-Google-Smtp-Source: ABhQp+R+vPFVohyTk2JIx62it0Kze3jLIFez+/V6aCSrPFu5m+AyM/ocFEP3VoRupHy9ehkxXhVFWQ== X-Received: by 10.107.163.15 with SMTP id m15mr18626997ioe.61.1508794933476; Mon, 23 Oct 2017 14:42:13 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:12 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 03/25] fscrypt: use FSCRYPT_* definitions, not FS_* Date: Mon, 23 Oct 2017 14:40:36 -0700 Message-Id: <20171023214058.128121-4-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Update the filesystem encryption kernel code to use the new names for the UAPI constants rather than the old names. Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- fs/crypto/fname.c | 4 ++-- fs/crypto/fscrypt_private.h | 4 ++-- fs/crypto/keyinfo.c | 36 +++++++++++++++++------------------- fs/crypto/policy.c | 14 +++++++------- include/linux/fscrypt.h | 8 ++++---- 5 files changed, 32 insertions(+), 34 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 2878289b3ed2..c91bcef65b9f 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -46,7 +46,7 @@ static int fname_encrypt(struct inode *inode, int res = 0; char iv[FS_CRYPTO_BLOCK_SIZE]; struct scatterlist sg; - int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); + int padding = 4 << (ci->ci_flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); unsigned int lim; unsigned int cryptlen; @@ -217,7 +217,7 @@ u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen) struct fscrypt_info *ci = inode->i_crypt_info; if (ci) - padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); + padding = 4 << (ci->ci_flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE); return round_up(ilen, padding); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index a180981ee6d7..5cb80a2d39ea 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -43,7 +43,7 @@ struct fscrypt_context { u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; - u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; } __packed; @@ -59,7 +59,7 @@ struct fscrypt_info { u8 ci_flags; struct crypto_skcipher *ci_ctfm; struct crypto_cipher *ci_essiv_tfm; - u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; + u8 ci_master_key[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; typedef enum { diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 7aed93bcfb82..ac41f646e7b7 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -38,7 +38,7 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) */ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], const struct fscrypt_key *source_key, - u8 derived_raw_key[FS_MAX_KEY_SIZE]) + u8 derived_raw_key[FSCRYPT_MAX_KEY_SIZE]) { int res = 0; struct skcipher_request *req = NULL; @@ -91,7 +91,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info, int res; description = kasprintf(GFP_NOFS, "%s%*phN", prefix, - FS_KEY_DESCRIPTOR_SIZE, + FSCRYPT_KEY_DESCRIPTOR_SIZE, ctx->master_key_descriptor); if (!description) return -ENOMEM; @@ -121,7 +121,8 @@ static int validate_user_key(struct fscrypt_info *crypt_info, master_key = (struct fscrypt_key *)ukp->data; BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); - if (master_key->size < min_keysize || master_key->size > FS_MAX_KEY_SIZE + if (master_key->size < min_keysize || + master_key->size > FSCRYPT_MAX_KEY_SIZE || master_key->size % AES_BLOCK_SIZE != 0) { printk_once(KERN_WARNING "%s: key size incorrect: %d\n", @@ -140,14 +141,10 @@ static const struct { const char *cipher_str; int keysize; } available_modes[] = { - [FS_ENCRYPTION_MODE_AES_256_XTS] = { "xts(aes)", - FS_AES_256_XTS_KEY_SIZE }, - [FS_ENCRYPTION_MODE_AES_256_CTS] = { "cts(cbc(aes))", - FS_AES_256_CTS_KEY_SIZE }, - [FS_ENCRYPTION_MODE_AES_128_CBC] = { "cbc(aes)", - FS_AES_128_CBC_KEY_SIZE }, - [FS_ENCRYPTION_MODE_AES_128_CTS] = { "cts(cbc(aes))", - FS_AES_128_CTS_KEY_SIZE }, + [FSCRYPT_MODE_AES_256_XTS] = { "xts(aes)", FS_AES_256_XTS_KEY_SIZE }, + [FSCRYPT_MODE_AES_256_CTS] = { "cts(cbc(aes))", FS_AES_256_CTS_KEY_SIZE }, + [FSCRYPT_MODE_AES_128_CBC] = { "cbc(aes)", FS_AES_128_CBC_KEY_SIZE }, + [FSCRYPT_MODE_AES_128_CTS] = { "cts(cbc(aes))", FS_AES_128_CTS_KEY_SIZE }, }; static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode, @@ -278,9 +275,10 @@ int fscrypt_get_encryption_info(struct inode *inode) /* Fake up a context for an unencrypted directory */ memset(&ctx, 0, sizeof(ctx)); ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; - ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; - memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE); + ctx.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; + memset(ctx.master_key_descriptor, 0x42, + FSCRYPT_KEY_DESCRIPTOR_SIZE); } else if (res != sizeof(ctx)) { return -EINVAL; } @@ -288,7 +286,7 @@ int fscrypt_get_encryption_info(struct inode *inode) if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) return -EINVAL; - if (ctx.flags & ~FS_POLICY_FLAGS_VALID) + if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID) return -EINVAL; crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS); @@ -312,12 +310,12 @@ int fscrypt_get_encryption_info(struct inode *inode) * crypto API as part of key derivation. */ res = -ENOMEM; - raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); + raw_key = kmalloc(FSCRYPT_MAX_KEY_SIZE, GFP_NOFS); if (!raw_key) goto out; - res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX, - keysize); + res = validate_user_key(crypt_info, &ctx, raw_key, + FSCRYPT_KEY_DESC_PREFIX, keysize); if (res && inode->i_sb->s_cop->key_prefix) { int res2 = validate_user_key(crypt_info, &ctx, raw_key, inode->i_sb->s_cop->key_prefix, @@ -349,7 +347,7 @@ int fscrypt_get_encryption_info(struct inode *inode) goto out; if (S_ISREG(inode->i_mode) && - crypt_info->ci_data_mode == FS_ENCRYPTION_MODE_AES_128_CBC) { + crypt_info->ci_data_mode == FSCRYPT_MODE_AES_128_CBC) { res = init_essiv_generator(crypt_info, raw_key, keysize); if (res) { pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n", diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 6a63b8a0d46c..19332a6fd52d 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -21,7 +21,7 @@ static bool is_encryption_context_consistent_with_policy( const struct fscrypt_policy *policy) { return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE) == 0 && + FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && (ctx->flags == policy->flags) && (ctx->contents_encryption_mode == policy->contents_encryption_mode) && @@ -36,13 +36,13 @@ static int create_encryption_context_from_policy(struct inode *inode, ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE); + FSCRYPT_KEY_DESCRIPTOR_SIZE); if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, policy->filenames_encryption_mode)) return -EINVAL; - if (policy->flags & ~FS_POLICY_FLAGS_VALID) + if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) return -EINVAL; ctx.contents_encryption_mode = policy->contents_encryption_mode; @@ -125,7 +125,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) policy.filenames_encryption_mode = ctx.filenames_encryption_mode; policy.flags = ctx.flags; memcpy(policy.master_key_descriptor, ctx.master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE); + FSCRYPT_KEY_DESCRIPTOR_SIZE); if (copy_to_user(arg, &policy, sizeof(policy))) return -EFAULT; @@ -199,7 +199,7 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) if (parent_ci && child_ci) { return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key, - FS_KEY_DESCRIPTOR_SIZE) == 0 && + FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && (parent_ci->ci_data_mode == child_ci->ci_data_mode) && (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && @@ -216,7 +216,7 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) return memcmp(parent_ctx.master_key_descriptor, child_ctx.master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE) == 0 && + FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && (parent_ctx.contents_encryption_mode == child_ctx.contents_encryption_mode) && (parent_ctx.filenames_encryption_mode == @@ -254,7 +254,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child, ctx.filenames_encryption_mode = ci->ci_filename_mode; ctx.flags = ci->ci_flags; memcpy(ctx.master_key_descriptor, ci->ci_master_key, - FS_KEY_DESCRIPTOR_SIZE); + FSCRYPT_KEY_DESCRIPTOR_SIZE); get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE); res = parent->i_sb->s_cop->set_context(child, &ctx, diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index f7aa7d62e235..671ce57e4673 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -99,12 +99,12 @@ static inline bool fscrypt_dummy_context_enabled(struct inode *inode) static inline bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode) { - if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && - filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) + if (contents_mode == FSCRYPT_MODE_AES_128_CBC && + filenames_mode == FSCRYPT_MODE_AES_128_CTS) return true; - if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && - filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) + if (contents_mode == FSCRYPT_MODE_AES_256_XTS && + filenames_mode == FSCRYPT_MODE_AES_256_CTS) return true; return false; From patchwork Mon Oct 23 21:40:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829651 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="cbujn3QE"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHT2pzDz9sRg for ; Tue, 24 Oct 2017 08:42:29 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932181AbdJWVm1 (ORCPT ); Mon, 23 Oct 2017 17:42:27 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:56924 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751525AbdJWVmQ (ORCPT ); Mon, 23 Oct 2017 17:42:16 -0400 Received: by mail-io0-f193.google.com with SMTP id m81so21685524ioi.13; Mon, 23 Oct 2017 14:42:15 -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=hUAs1fwaOUa8W4Qac1pvK3n8sY4bEQX/091zabAwlIc=; b=cbujn3QE4gDLX7eiJTuGmAWtzSIo5Jhw8IzirsPNOqgHJoYrJ7m83lZZh/Fd3VgM/p jbf5vDt2g32lc58j+jMlhZltcABxp36d22z7A1Q0/a04wcpxzDiGrcKGv31tktFxDHLU gCLtYMWfwTqngHFe58O/cMAIUtJpJ8/Xh015kFMxm/lWWATVCeBqCwxNN4nqIm7aku6C r9IMCaJKJlrdxC08NTvsFFM7p7wOU6VPwhRQ9fDTFFfO/aihv8Nm5VrU05vH8HLcfmD6 HPOvf9j4+9CryHIAiNPZ0SjdiO4m+VESnamTXA/iS/1ZdL3633DOj3yfgaE6havskCdT EN/A== 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=hUAs1fwaOUa8W4Qac1pvK3n8sY4bEQX/091zabAwlIc=; b=EW7ffx9jlWstTbsoLuVzezam83C08AXoFYjKnOXXlUeXAj61h27pO0UElJ+ml1rDP7 eNJAWLCFBA+c/r+pbqPvk/mlF0CLDwAfxFctinx5tgoIDG0SzxyT5WpuxMA4oXWeZIDp wi1JG8rhidxoXdcQkVPKC628Hb1fkp4K+T62taTF7Obkkwpn58KyC/CMgk+rW+G3cSlz f114gH0NS7Aqvradm4AKWbIi9nBX0pQb5kOLXkpBa8liJQp64hhJv5rNIM7l4RtwnayS ag+laOAuUf6ZGJmmC1lkp6cwwyvsT/6pvRhON5hUhFHDM0fXE7haTdtiDu6qzkCZvTjD pikA== X-Gm-Message-State: AMCzsaV1FmAvksXDGCLE/0yWcxpYIxxP2NTcL0VPTAfSYeL1vYWTImoK sePuc1yaVhfb5a2YxWj6rJB9Qba/ X-Google-Smtp-Source: ABhQp+RkcT5Kk9lxFMUwEcEzYlMdg/ZwfbMj3T7qPAwz64C2N51kjhABrraM0Rxg7rKWAqcU3KY3YA== X-Received: by 10.107.35.15 with SMTP id j15mr1940112ioj.241.1508794934755; Mon, 23 Oct 2017 14:42:14 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:14 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 04/25] fscrypt: refactor finding and deriving key Date: Mon, 23 Oct 2017 14:40:37 -0700 Message-Id: <20171023214058.128121-5-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers In preparation for introducing a new way to find the master keys and derive the per-file keys, clean up the current method. This includes: - Introduce a helper function find_and_derive_key() so that we don't have to add more code directly to fscrypt_get_encryption_info(). - Don't pass the 'struct fscrypt_key' directly into derive_key_aes(). This is in preparation for the case where we find the master key in a filesystem-level keyring, where (for good reasons) the key payload will *not* be formatted as the UAPI 'struct fscrypt_key'. - Separate finding the key from key derivation. In particular, it *only* makes sense to fall back to the alternate key description prefix if searching for the "fscrypt:" prefix returns -ENOKEY. It doesn't make sense to do so when derive_key_aes() fails, for example. - Improve the error messages for when the fscrypt_key is invalid. - Rename 'raw_key' to 'derived_key' for clarity. Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- fs/crypto/keyinfo.c | 205 ++++++++++++++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 96 deletions(-) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index ac41f646e7b7..d3a97c2cd4dd 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -28,113 +28,138 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) complete(&ecr->completion); } -/** - * derive_key_aes() - Derive a key using AES-128-ECB - * @deriving_key: Encryption key used for derivation. - * @source_key: Source key to which to apply derivation. - * @derived_raw_key: Derived raw key. +/* + * Key derivation function. This generates the derived key by encrypting the + * master key with AES-128-ECB using the nonce as the AES key. * - * Return: Zero on success; non-zero otherwise. + * The master key must be at least as long as the derived key. If the master + * key is longer, then only the first 'derived_keysize' bytes are used. */ -static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], - const struct fscrypt_key *source_key, - u8 derived_raw_key[FSCRYPT_MAX_KEY_SIZE]) +static int derive_key_aes(const u8 *master_key, + const struct fscrypt_context *ctx, + u8 *derived_key, unsigned int derived_keysize) { - int res = 0; + int err; struct skcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist src_sg, dst_sg; - struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0); + struct crypto_skcipher *tfm; + + tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); - if (IS_ERR(tfm)) { - res = PTR_ERR(tfm); - tfm = NULL; - goto out; - } crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY); req = skcipher_request_alloc(tfm, GFP_NOFS); if (!req) { - res = -ENOMEM; + err = -ENOMEM; goto out; } skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, derive_crypt_complete, &ecr); - res = crypto_skcipher_setkey(tfm, deriving_key, - FS_AES_128_ECB_KEY_SIZE); - if (res < 0) + + BUILD_BUG_ON(sizeof(ctx->nonce) != FS_AES_128_ECB_KEY_SIZE); + err = crypto_skcipher_setkey(tfm, ctx->nonce, sizeof(ctx->nonce)); + if (err) goto out; - sg_init_one(&src_sg, source_key->raw, source_key->size); - sg_init_one(&dst_sg, derived_raw_key, source_key->size); - skcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size, + sg_init_one(&src_sg, master_key, derived_keysize); + sg_init_one(&dst_sg, derived_key, derived_keysize); + skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize, NULL); - res = crypto_skcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { + err = crypto_skcipher_encrypt(req); + if (err == -EINPROGRESS || err == -EBUSY) { wait_for_completion(&ecr.completion); - res = ecr.res; + err = ecr.res; } out: skcipher_request_free(req); crypto_free_skcipher(tfm); - return res; + return err; } -static int validate_user_key(struct fscrypt_info *crypt_info, - struct fscrypt_context *ctx, u8 *raw_key, - const char *prefix, int min_keysize) +/* + * Search the current task's subscribed keyrings for a "logon" key with + * description prefix:descriptor, and if found acquire a read lock on it and + * return a pointer to its validated payload in *payload_ret. + */ +static struct key * +find_and_lock_process_key(const char *prefix, + const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE], + unsigned int min_keysize, + const struct fscrypt_key **payload_ret) { char *description; - struct key *keyring_key; - struct fscrypt_key *master_key; + struct key *key; const struct user_key_payload *ukp; - int res; + const struct fscrypt_key *payload; description = kasprintf(GFP_NOFS, "%s%*phN", prefix, - FSCRYPT_KEY_DESCRIPTOR_SIZE, - ctx->master_key_descriptor); + FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor); if (!description) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - keyring_key = request_key(&key_type_logon, description, NULL); + key = request_key(&key_type_logon, description, NULL); kfree(description); - if (IS_ERR(keyring_key)) - return PTR_ERR(keyring_key); - down_read(&keyring_key->sem); - - if (keyring_key->type != &key_type_logon) { - printk_once(KERN_WARNING - "%s: key type must be logon\n", __func__); - res = -ENOKEY; - goto out; - } - ukp = user_key_payload_locked(keyring_key); - if (!ukp) { - /* key was revoked before we acquired its semaphore */ - res = -EKEYREVOKED; - goto out; + if (IS_ERR(key)) + return key; + + down_read(&key->sem); + ukp = user_key_payload_locked(key); + + if (!ukp) /* was the key revoked before we acquired its semaphore? */ + goto invalid; + + payload = (const struct fscrypt_key *)ukp->data; + + if (ukp->datalen != sizeof(struct fscrypt_key) || + payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) { + pr_warn_ratelimited("fscrypt: key with description '%s' has invalid payload\n", + key->description); + goto invalid; } - if (ukp->datalen != sizeof(struct fscrypt_key)) { - res = -EINVAL; - goto out; + + if (payload->size < min_keysize) { + pr_warn_ratelimited("fscrypt: key with description '%s' is too short " + "(got %u bytes, need %u+ bytes)\n", + key->description, + payload->size, min_keysize); + goto invalid; } - master_key = (struct fscrypt_key *)ukp->data; - BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); - - if (master_key->size < min_keysize || - master_key->size > FSCRYPT_MAX_KEY_SIZE - || master_key->size % AES_BLOCK_SIZE != 0) { - printk_once(KERN_WARNING - "%s: key size incorrect: %d\n", - __func__, master_key->size); - res = -ENOKEY; - goto out; + + *payload_ret = payload; + return key; + +invalid: + up_read(&key->sem); + key_put(key); + return ERR_PTR(-ENOKEY); +} + +/* Find the master key, then derive the inode's actual encryption key */ +static int find_and_derive_key(const struct inode *inode, + const struct fscrypt_context *ctx, + u8 *derived_key, unsigned int derived_keysize) +{ + struct key *key; + const struct fscrypt_key *payload; + int err; + + key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX, + ctx->master_key_descriptor, + derived_keysize, &payload); + if (key == ERR_PTR(-ENOKEY) && inode->i_sb->s_cop->key_prefix) { + key = find_and_lock_process_key(inode->i_sb->s_cop->key_prefix, + ctx->master_key_descriptor, + derived_keysize, &payload); } - res = derive_key_aes(ctx->nonce, master_key, raw_key); -out: - up_read(&keyring_key->sem); - key_put(keyring_key); - return res; + if (IS_ERR(key)) + return PTR_ERR(key); + err = derive_key_aes(payload->raw, ctx, derived_key, derived_keysize); + up_read(&key->sem); + key_put(key); + return err; } static const struct { @@ -256,8 +281,8 @@ int fscrypt_get_encryption_info(struct inode *inode) struct fscrypt_context ctx; struct crypto_skcipher *ctfm; const char *cipher_str; - int keysize; - u8 *raw_key = NULL; + unsigned int derived_keysize; + u8 *derived_key = NULL; int res; if (inode->i_crypt_info) @@ -301,7 +326,8 @@ int fscrypt_get_encryption_info(struct inode *inode) memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); - res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize); + res = determine_cipher_type(crypt_info, inode, + &cipher_str, &derived_keysize); if (res) goto out; @@ -310,24 +336,14 @@ int fscrypt_get_encryption_info(struct inode *inode) * crypto API as part of key derivation. */ res = -ENOMEM; - raw_key = kmalloc(FSCRYPT_MAX_KEY_SIZE, GFP_NOFS); - if (!raw_key) + derived_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); + if (!derived_key) goto out; - res = validate_user_key(crypt_info, &ctx, raw_key, - FSCRYPT_KEY_DESC_PREFIX, keysize); - if (res && inode->i_sb->s_cop->key_prefix) { - int res2 = validate_user_key(crypt_info, &ctx, raw_key, - inode->i_sb->s_cop->key_prefix, - keysize); - if (res2) { - if (res2 == -ENOKEY) - res = -ENOKEY; - goto out; - } - } else if (res) { + res = find_and_derive_key(inode, &ctx, derived_key, derived_keysize); + if (res) goto out; - } + ctfm = crypto_alloc_skcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; @@ -338,17 +354,14 @@ int fscrypt_get_encryption_info(struct inode *inode) crypt_info->ci_ctfm = ctfm; crypto_skcipher_clear_flags(ctfm, ~0); crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY); - /* - * if the provided key is longer than keysize, we use the first - * keysize bytes of the derived key only - */ - res = crypto_skcipher_setkey(ctfm, raw_key, keysize); + res = crypto_skcipher_setkey(ctfm, derived_key, derived_keysize); if (res) goto out; if (S_ISREG(inode->i_mode) && crypt_info->ci_data_mode == FSCRYPT_MODE_AES_128_CBC) { - res = init_essiv_generator(crypt_info, raw_key, keysize); + res = init_essiv_generator(crypt_info, derived_key, + derived_keysize); if (res) { pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n", __func__, res, inode->i_ino); @@ -361,7 +374,7 @@ int fscrypt_get_encryption_info(struct inode *inode) if (res == -ENOKEY) res = 0; put_crypt_info(crypt_info); - kzfree(raw_key); + kzfree(derived_key); return res; } EXPORT_SYMBOL(fscrypt_get_encryption_info); From patchwork Mon Oct 23 21:40:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829677 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="cImZbeoA"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKd6XBZz9sPt for ; Tue, 24 Oct 2017 08:44:21 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932382AbdJWVoT (ORCPT ); Mon, 23 Oct 2017 17:44:19 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:47155 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751552AbdJWVmR (ORCPT ); Mon, 23 Oct 2017 17:42:17 -0400 Received: by mail-io0-f193.google.com with SMTP id h70so21696165ioi.4; Mon, 23 Oct 2017 14:42:16 -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=ZXxOc7bqPylR64/IGbno9751CRE5IjhvJXc3eyHiJr4=; b=cImZbeoAXAqKDPzwkRlrnhnpZeSCmY7sRHXfilOoPudPirEzMXQF5wM/z48MAloIbz uR/a7KLJNq10czGWuwmHEorwezS62vcqSwUowyqZBXg7/lFllzsS1xYSGMIFmVvs8awn cSpIOmiteL9Uf6FSL24ip2hIZQwcAHpuzZdBkO+p5davhu9Wu108A7YvJK5tAlX8QqgC vcLqDRODWqGSHUNKx3BzYS7ozFIt1QzmauLQA8ksuvstnxOYRAdNbGFfWs6itKQTbZOc z1FeuA42qmvx0YYKmcDep77HfuikYi2M/8UGbDgya2FZX6Jgoqn+SZKU0LAMteiWqAK6 3vtA== 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=ZXxOc7bqPylR64/IGbno9751CRE5IjhvJXc3eyHiJr4=; b=ozQPNo3M/Hf6gQsWw/fBT71+Yioh98kmhQYwSFn/iQw/Oe3y85+Pbi7DLqHL3AHFbM AZu9dUUaGBcbRrT5uT6c9bc7wV1ba1XYjlqEiFZm6nMYjgzWOqciAFOOvEqcYx2hGtFw a0Dk+9apYVzmHmxYRpx59CSTPMIgP/ZH/zz/ZRwR6J2BbywEE0dDJ4HFEbhj/jwqZ0PD 9Og75xePMnKx2vwmzZRPeyrylYsLUfx+Tq5xTPmzX9DI/OtvtNB8KUmDo5xe3FNhGH4l WwGu0BiNjvjCfVXA3ugagdT0j2NsaNE9St9T1EHrniCNVxvVKcbyEak7iGETDL9VXKQu Spdg== X-Gm-Message-State: AMCzsaUwpTIUz/38eT+Bw1AK5S7WtHPXBcAdnfF6EVMZC07nfnyfSKXR ZwFTgxGSGPnznmp9VW2CDSqbycKA X-Google-Smtp-Source: ABhQp+TwQzJyNtN5jhj0ZqvqV/Vw+qAuFsdMtc+iLIIC/LN26T6ql8elWydDLgrxUzs95v9InBGKag== X-Received: by 10.107.137.23 with SMTP id l23mr18083039iod.138.1508794936035; Mon, 23 Oct 2017 14:42:16 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:15 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 05/25] fs: add ->s_master_keys to struct super_block Date: Mon, 23 Oct 2017 14:40:38 -0700 Message-Id: <20171023214058.128121-6-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Add an ->s_master_keys keyring to 'struct super_block' for holding encryption keys which have been added to the filesystem. This keyring will be populated using a new fscrypt ioctl. This is needed for several reasons, including: - To solve the visibility problems of having filesystem encryption keys stored in process-subscribed keyrings, while the VFS state of the filesystem is actually global. - To implement a proper API for removing keys, which among other things will require maintaining the list of inodes that are using each master key so that we can evict the inodes when the key is removed. - To allow caching a crypto transform for each master key so that we don't have to repeatedly allocate one over and over. See later patches for full details, including why it wouldn't be enough to add the concept of a "global keyring" to the keyrings API instead. ->s_master_keys will only be allocated when someone tries to add a key for the first time. Otherwise it will stay NULL. Note that this could go in the filesystem-specific superblocks instead. However, we already have three filesystems using fs/crypto/, so it's useful to have it in the VFS. Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- fs/super.c | 3 +++ include/linux/fs.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/fs/super.c b/fs/super.c index 166c4ee0d0ed..161a9d05aa9f 100644 --- a/fs/super.c +++ b/fs/super.c @@ -168,6 +168,9 @@ static void destroy_super(struct super_block *s) WARN_ON(!list_empty(&s->s_mounts)); put_user_ns(s->s_user_ns); kfree(s->s_subtype); +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + key_put(s->s_master_keys); +#endif call_rcu(&s->rcu, destroy_super_rcu); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 3efd5ded21c9..8cfb0877d32c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1440,6 +1440,10 @@ struct super_block { spinlock_t s_inode_wblist_lock; struct list_head s_inodes_wb; /* writeback inodes */ + +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + struct key *s_master_keys; /* master crypto keys in use */ +#endif } __randomize_layout; /* Helper functions so that in most cases filesystems will From patchwork Mon Oct 23 21:40:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829678 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="kovZ8+Cs"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKk2Jj0z9sPt for ; Tue, 24 Oct 2017 08:44:26 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932372AbdJWVoP (ORCPT ); Mon, 23 Oct 2017 17:44:15 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:44351 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751465AbdJWVmT (ORCPT ); Mon, 23 Oct 2017 17:42:19 -0400 Received: by mail-io0-f193.google.com with SMTP id m16so21742707iod.1; Mon, 23 Oct 2017 14:42:18 -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=TdGJTgVUCqRKWTGQ9RKIi92RQ/5OTQV3UxQAmtlkWQs=; b=kovZ8+CsHG81pmh8o8BWUeOKDjFKwPFsDuIAp70wZYq0CIEDEY5XODiVue2/8pQY2R F1YkWSRw5Au55qMtCUeFqlfQj3zL703JRFeNHbDDvBtEU19dJQ7ZwOMaulitvNgLcYdE m225cu4l+Srs/m8JwFx5p/aIHpld6vktLqTtByMyIhyNPg/pqASLSiLsOM9yNCjbJ8/B UQengZl2vgBVw5vl9l9X52DYrnXvuG3JQyyfSy81hqbFHPmI/uk6sZEQRVdR7jUOGelG 5PnS+IusnPkywB5ynIoJX+7aRN61yVlsK38ak55KQCzwDy7+s9vE4sTSv97I+M9XPEdc srsg== 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=TdGJTgVUCqRKWTGQ9RKIi92RQ/5OTQV3UxQAmtlkWQs=; b=Pw5CNFgEt4YrdMCUYaXKqW33o8kmYOCKcIj9M1u+CCwh8kCGKpFpD3bbtYvqjtZrsK z4lKSe5nqVzLaGJXAzdoL1XOhA5r0qEZOk4vl2rzhDg9eqLvpE0/D1RdVBEoa3vXB2EL 10rNMGvD0JKo3302VZ77Y4ogeKtH/SfEJi2yqyDkDhOeF5ChMh1Xeq3rnBYG5H8hiBoL VL8/KsqXFjXvqvrsplCA9qnczNeZPtggQ2kAvjixT6g5pFEpUFR1zTkF7+6zjHOIOo+x aqEK6J4+dSGESJraATypwVjKH6ooBti3qdDpY9+oMoRqTOQxtYnpr8oF/Ak/yncq1GRF f4bQ== X-Gm-Message-State: AMCzsaUvGMow7hroBRU72MgaA3Sbs0T3ZKl93f/qtJw9ivgj6WB9W2N9 t2RiQaes/qOPJY5fx6jJynyKnW/9 X-Google-Smtp-Source: ABhQp+SFbpeo1mlWFnDzN3faHBF5sgmn0s5XRk8DziN1cQGh7LAK1ReG+F05b5V5YsRCwn56hJWpig== X-Received: by 10.107.10.82 with SMTP id u79mr19423371ioi.252.1508794937485; Mon, 23 Oct 2017 14:42:17 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:16 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 06/25] fscrypt: add FS_IOC_ADD_ENCRYPTION_KEY ioctl Date: Mon, 23 Oct 2017 14:40:39 -0700 Message-Id: <20171023214058.128121-7-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Add a new filesystem encryption ioctl, FS_IOC_ADD_ENCRYPTION_KEY. This ioctl adds a master encryption key to the filesystem encryption keyring ->s_master_keys. When a process tries to access an encrypted file that has not yet been "unlocked" (set up with an ->i_crypt_info containing a crypto transform keyed by the file's derived key), fscrypt_get_encryption_info() will search for the master key in ->s_master_keys before falling back to the process-subscribed keyrings. For now this ioctl is root-only, which is necessary in part because the keys are identified by master_key_descriptor, which is not cryptographically tied to the actual key payload. However, a later patch will introduce a new encryption policy version where the key is identified by a cryptographic hash. This will, in combination with other protections, make it possible for non-root users to use this ioctl in some situations. Why we need this ~~~~~~~~~~~~~~~~ The main problem is that the "locked/unlocked" (ciphertext/plaintext) status of encrypted files is global, but currently the master keys are not. We only look for master keys in the process-subscribed keyrings; that is, the current thread keyring, process keyring, and session keyring, where the session keyring usually contains the user keyring. This means we have to put the master keys in the keyrings for individual users or for individual sessions. This causes much confusion as soon as a process with a different UID, such as a 'sudo' command, tries to access encrypted files. In such a situation, whether each individual inode appears "locked" or "unlocked" will depend on whether it was previously accessed and happens to still be in the inode cache, which is more or less nondeterministic. It may seem that we should indeed provide each process its own "view" of the filesystem depending on whether it "has the key" or not. However that would be extremely difficult to do without a separate mount, due to the way the VFS caches work. Furthermore, it is actually missing the point of encryption because it would *not* be encryption that would provide the different "views", but rather kernel *code*. Thus, it would simply be an access control mechanism largely redundant with the many existing access control mechanisms such as UNIX file permissions and LSMs. The reality is that the confidentially of encrypted files *after the kernel already has the encryption key in memory* is only protected by the correctness of the kernel, not by the mathematical properties of encryption. And at the end of the day, almost all users of filesystem encryption we are aware of do really need the global view, because they need encrypted files to be accessible to processes running under different UIDs. This can be as simple as needing to be able to run 'sudo', or it can be something more complex like Android's key management system where applications running under different UIDs as well as system processes need access to the same encrypted files. As a result, some very ugly hacks have been added to try to emulate globally visible keys. The Android and Chromium OS key management systems simply create a "session" keyring in PID 1 and put all the keys in it, which abuses the "session" keyring to have nothing to do with a "session", but rather be a global keyring. This is fragile, as it means that the "session" keyring must never be changed. There have also been bugs involving processes that were forked before the "session" keyring was created, causing them to miss out on the keys. Meanwhile, filesystem encryption tools written for general-purpose Linux distributions have no such ability to abuse the "session" keyring. They instead must implement "interesting" workarounds such as linking all the user keyrings into root's user keyring, as is done by the fscrypt userspace tool (see the design document at https://goo.gl/55cCrI). This raises security concerns, to say the least. By having an API to add a key to the *filesystem* we'll be able to eliminate all the above hacks and better express the intended semantics: the "locked/unlocked" status of an encrypted directory is global. And orthogonally to encryption, existing mechanisms such as file permissions and LSMs can and should continue to be used for the purpose of *access control*. Why use a custom key type ~~~~~~~~~~~~~~~~~~~~~~~~~ The keys the new ioctl adds to ->s_master_keys are still "keys" in the sense of the keyrings service, but they have a custom key type rather than the "logon" key type we currently require when userspace provides a key via a process-subscribed keyring. Judging just from this patch alone, the "logon" key type would be sufficient. However, later patches will be solving problems such as the nonstandard KDF and the lack of a key removal API. The solutions for these problems will require tracking information on a per-master-key basis. Therefore, we'll need a custom structure associated with each master key anyway. A custom key type lets us do that easily. Why not use add_key() ~~~~~~~~~~~~~~~~~~~~~ Instead of adding a new ioctl() to add a key, we could have userspace use the add_key() system call. In combination with an ioctl which retrieves the key ID of ->s_master_keys, add_key() could be used to add a key to ->s_master_keys. Alternatively, we could add the concept of a "global keyring" or "namespace keyring" to the keyrings service, where that keyring would be searched in addition to the process-subscribed keyrings. Then, add_key() could add a key to that. This actually makes sense given only the present patch. However, unfortunately it falls apart once we consider the follow-on changes. First, we also need to add the ability to remove an encryption key, and it will need to have more specialized semantics than keyctl_unlink() or keyctl_revoke() can provide. For example, we must not only wipe the master key *secret* from memory, but we must also try to evict all the inodes which had been "unlocked" using the key. And it's possible that even though the master key secret was wiped, some inodes could not be evicted, since they may be busy. In that case, we still want to wipe the master key *secret* so that no more encrypted files can be "unlocked". But we also want to allow userspace to retry the request later, so that evicting the remaining inodes can be re-attempted. Alternatively, we want the same list of inodes to be picked up again if the secret happens to be added again. Trying to shoehorn these specific semantics into the keyrings API would be very difficult. Later we also want to open up the add/remove key operations to non-root users by taking advantage of a new encryption policy version which includes a cryptographic hash of the master key. This is needed because otherwise we wouldn't be able to fully replace the process-subscribed keyrings and avoid all its problems mentioned earlier. But to actually make non-root use secure, we'll need to do some extra accounting where we keep track of all users who have added a given key, then only really remove a key after all users have removed it. Non-root users also cannot simply be given write permission to a global keyring. So again, it seems that trying to shoehorn the needed semantics into the keyrings API would just create problems. Nevertheless, we do still use the keyrings service internally so that we reuse some code and get some "free" functionality such as having the keys show up in /proc/keys for debugging purposes. Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- fs/crypto/crypto.c | 12 +- fs/crypto/fscrypt_private.h | 3 + fs/crypto/keyinfo.c | 351 +++++++++++++++++++++++++++++++++++++++- include/linux/fscrypt_notsupp.h | 5 + include/linux/fscrypt_supp.h | 1 + include/uapi/linux/fscrypt.h | 41 +++-- 6 files changed, 397 insertions(+), 16 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 608f6bbe0f31..489c504ac20d 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -449,6 +450,8 @@ int fscrypt_initialize(unsigned int cop_flags) */ static int __init fscrypt_init(void) { + int err = -ENOMEM; + fscrypt_read_workqueue = alloc_workqueue("fscrypt_read_queue", WQ_HIGHPRI, 0); if (!fscrypt_read_workqueue) @@ -462,14 +465,20 @@ static int __init fscrypt_init(void) if (!fscrypt_info_cachep) goto fail_free_ctx; + err = register_key_type(&key_type_fscrypt_mk); + if (err) + goto fail_free_info; + return 0; +fail_free_info: + kmem_cache_destroy(fscrypt_info_cachep); fail_free_ctx: kmem_cache_destroy(fscrypt_ctx_cachep); fail_free_queue: destroy_workqueue(fscrypt_read_workqueue); fail: - return -ENOMEM; + return err; } module_init(fscrypt_init) @@ -484,6 +493,7 @@ static void __exit fscrypt_exit(void) destroy_workqueue(fscrypt_read_workqueue); kmem_cache_destroy(fscrypt_ctx_cachep); kmem_cache_destroy(fscrypt_info_cachep); + unregister_key_type(&key_type_fscrypt_mk); fscrypt_essiv_cleanup(); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 5cb80a2d39ea..b2fad12eeedb 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -27,6 +27,8 @@ #define FS_KEY_DERIVATION_NONCE_SIZE 16 +#define FSCRYPT_MIN_KEY_SIZE 16 + /** * Encryption context for inode * @@ -93,6 +95,7 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); /* keyinfo.c */ +extern struct key_type key_type_fscrypt_mk; extern void __exit fscrypt_essiv_cleanup(void); #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index d3a97c2cd4dd..3f1cb8bbc1e5 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -9,14 +9,307 @@ */ #include -#include +#include #include +#include +#include #include #include #include "fscrypt_private.h" static struct crypto_shash *essiv_hash_tfm; +/* + * fscrypt_master_key_secret - secret key material of an in-use master key + */ +struct fscrypt_master_key_secret { + + /* Size of the raw key in bytes */ + u32 size; + + /* The raw key */ + u8 raw[FSCRYPT_MAX_KEY_SIZE]; +}; + +/* + * fscrypt_master_key - an in-use master key + * + * This represents a master encryption key which has been added to the + * filesystem and can be used to "unlock" the encrypted files which were + * encrypted with it. + */ +struct fscrypt_master_key { + + /* The secret key material */ + struct fscrypt_master_key_secret mk_secret; + + /* Arbitrary key descriptor which was assigned by userspace */ + struct fscrypt_key_specifier mk_spec; +}; + +static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec) +{ + switch (spec->type) { + case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: + return FSCRYPT_KEY_DESCRIPTOR_SIZE; + } + return 0; +} + +static inline bool valid_key_spec(const struct fscrypt_key_specifier *spec) +{ + if (spec->reserved) + return false; + return master_key_spec_len(spec) != 0; +} + +static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret) +{ + memzero_explicit(secret, sizeof(*secret)); +} + +static void move_master_key_secret(struct fscrypt_master_key_secret *dst, + struct fscrypt_master_key_secret *src) +{ + memcpy(dst, src, sizeof(*dst)); + memzero_explicit(src, sizeof(*src)); +} + +static void free_master_key(struct fscrypt_master_key *mk) +{ + wipe_master_key_secret(&mk->mk_secret); + kzfree(mk); +} + +static int fscrypt_key_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + key->payload.data[0] = (struct fscrypt_master_key *)prep->data; + return 0; +} + +static void fscrypt_key_destroy(struct key *key) +{ + free_master_key(key->payload.data[0]); +} + +static void fscrypt_key_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); +} + +/* + * Type of key in ->s_master_keys. Each key of this type represents a master + * key which has been added to the filesystem. Its payload is a + * 'struct fscrypt_master_key'. + */ +struct key_type key_type_fscrypt_mk = { + .name = "._fscrypt", + .instantiate = fscrypt_key_instantiate, + .destroy = fscrypt_key_destroy, + .describe = fscrypt_key_describe, +}; + +/* + * Search ->s_master_keys. Note that we mark the keyring reference as + * "possessed" so that we can use the KEY_POS_SEARCH permission. + */ +static struct key *search_fscrypt_keyring(struct key *keyring, + struct key_type *type, + const char *description) +{ + key_ref_t keyref; + + keyref = keyring_search(make_key_ref(keyring, 1), type, description); + if (IS_ERR(keyref)) { + if (PTR_ERR(keyref) == -EAGAIN) + keyref = ERR_PTR(-ENOKEY); + return ERR_CAST(keyref); + } + return key_ref_to_ptr(keyref); +} + +#define FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE \ + (sizeof("fscrypt-") - 1 + sizeof(((struct super_block *)0)->s_id) + 1) + +#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1) + +static void format_fs_keyring_description( + char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE], + const struct super_block *sb) +{ + sprintf(description, "fscrypt-%s", sb->s_id); +} + +static void format_mk_description( + char description[FSCRYPT_MK_DESCRIPTION_SIZE], + const struct fscrypt_key_specifier *mk_spec) +{ + sprintf(description, "%*phN", + master_key_spec_len(mk_spec), mk_spec->max_specifier); +} + +/* + * Find the specified master key in ->s_master_keys. + * Returns ERR_PTR(-ENOKEY) if not found. + */ +static struct key *find_master_key(struct super_block *sb, + const struct fscrypt_key_specifier *mk_spec) +{ + struct key *keyring; + char description[FSCRYPT_MK_DESCRIPTION_SIZE]; + + /* pairs with smp_store_release() in add_to_filesystem_keyring() */ + keyring = smp_load_acquire(&sb->s_master_keys); + if (keyring == NULL) + return ERR_PTR(-ENOKEY); + + format_mk_description(description, mk_spec); + return search_fscrypt_keyring(keyring, &key_type_fscrypt_mk, + description); +} + +static struct key * +allocate_master_key(struct fscrypt_master_key_secret *secret, + const struct fscrypt_key_specifier *mk_spec) +{ + struct fscrypt_master_key *mk; + struct key *key; + char description[FSCRYPT_MK_DESCRIPTION_SIZE]; + int err; + + mk = kzalloc(sizeof(*mk), GFP_NOFS); + if (!mk) + return ERR_PTR(-ENOMEM); + + mk->mk_spec = *mk_spec; + + move_master_key_secret(&mk->mk_secret, secret); + + format_mk_description(description, mk_spec); + key = key_alloc(&key_type_fscrypt_mk, description, + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), + KEY_POS_SEARCH | KEY_USR_SEARCH | + KEY_USR_READ | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(key)) + goto out_free_mk; + + err = key_instantiate_and_link(key, mk, sizeof(*mk), NULL, NULL); + if (err) { + key_put(key); + key = ERR_PTR(err); + goto out_free_mk; + } + return key; + +out_free_mk: + free_master_key(mk); + return key; +} + +/* + * Add the given key to ->s_master_keys, creating ->s_master_keys if it doesn't + * already exist. Synchronized by fscrypt_add_key_mutex. + */ +static int add_to_filesystem_keyring(struct super_block *sb, struct key *key) +{ + struct key *keyring = sb->s_master_keys; + + if (keyring == NULL) { + char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE]; + + format_fs_keyring_description(description, sb); + keyring = keyring_alloc(description, GLOBAL_ROOT_UID, + GLOBAL_ROOT_GID, current_cred(), + KEY_POS_SEARCH | KEY_USR_SEARCH | + KEY_USR_READ | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) + return PTR_ERR(keyring); + + /* Pairs with smp_load_acquire() in find_master_key() */ + smp_store_release(&sb->s_master_keys, keyring); + } + + return key_link(keyring, key); +} + +static int add_master_key(struct super_block *sb, + struct fscrypt_master_key_secret *secret, + const struct fscrypt_key_specifier *mk_spec) +{ + struct key *key; + int err; + static DEFINE_MUTEX(fscrypt_add_key_mutex); + + mutex_lock(&fscrypt_add_key_mutex); /* serialize find + link */ + key = find_master_key(sb, mk_spec); + if (IS_ERR(key)) { + if (key != ERR_PTR(-ENOKEY)) { + err = PTR_ERR(key); + goto out_unlock; + } + /* Didn't find the key in ->s_master_keys; add it. */ + + key = allocate_master_key(secret, mk_spec); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto out_unlock; + } + err = add_to_filesystem_keyring(sb, key); + if (err) + goto out_put_key; + } + err = 0; +out_put_key: + key_put(key); +out_unlock: + mutex_unlock(&fscrypt_add_key_mutex); + return err; +} + +/* + * Add a master encryption key to the filesystem, causing all files which were + * encrypted with it to appear "unlocked" (decrypted) when accessed. + */ +int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct fscrypt_add_key_args __user *uarg = _uarg; + struct fscrypt_add_key_args arg; + struct fscrypt_master_key_secret secret; + int err; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || + arg.raw_size > FSCRYPT_MAX_KEY_SIZE) + return -EINVAL; + + if (arg.reserved1 || + memchr_inv(arg.reserved2, 0, sizeof(arg.reserved2))) + return -EINVAL; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + memset(&secret, 0, sizeof(secret)); + secret.size = arg.raw_size; + err = -EFAULT; + if (copy_from_user(secret.raw, uarg->raw, secret.size)) + goto out_wipe_secret; + + err = add_master_key(sb, &secret, &arg.key_spec); +out_wipe_secret: + wipe_master_key_secret(&secret); + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); + static void derive_crypt_complete(struct crypto_async_request *req, int rc) { struct fscrypt_completion_result *ecr = req->data; @@ -137,10 +430,10 @@ find_and_lock_process_key(const char *prefix, return ERR_PTR(-ENOKEY); } -/* Find the master key, then derive the inode's actual encryption key */ -static int find_and_derive_key(const struct inode *inode, - const struct fscrypt_context *ctx, - u8 *derived_key, unsigned int derived_keysize) +static int find_and_derive_key_legacy(const struct inode *inode, + const struct fscrypt_context *ctx, + u8 *derived_key, + unsigned int derived_keysize) { struct key *key; const struct fscrypt_key *payload; @@ -162,6 +455,54 @@ static int find_and_derive_key(const struct inode *inode, return err; } +/* Find the master key, then derive the inode's actual encryption key */ +static int find_and_derive_key(const struct inode *inode, + const struct fscrypt_context *ctx, + u8 *derived_key, unsigned int derived_keysize) +{ + struct key *key; + struct fscrypt_master_key *mk; + struct fscrypt_key_specifier mk_spec; + int err; + + mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; + memcpy(mk_spec.descriptor, ctx->master_key_descriptor, + FSCRYPT_KEY_DESCRIPTOR_SIZE); + + key = find_master_key(inode->i_sb, &mk_spec); + if (IS_ERR(key)) { + if (key != ERR_PTR(-ENOKEY)) + return PTR_ERR(key); + /* + * As a legacy fallback, we search the current task's subscribed + * keyrings in addition to ->s_master_keys. + */ + return find_and_derive_key_legacy(inode, ctx, derived_key, + derived_keysize); + } + mk = key->payload.data[0]; + + /* + * Require that the master key be at least as long as the derived key. + * Otherwise, the derived key cannot possibly contain as much entropy as + * that required by the encryption mode it will be used for. + */ + if (mk->mk_secret.size < derived_keysize) { + pr_warn_ratelimited("fscrypt: key with description '%s' is too short " + "(got %u bytes, need %u+ bytes)\n", + key->description, + mk->mk_secret.size, derived_keysize); + err = -ENOKEY; + goto out_put_key; + } + + err = derive_key_aes(mk->mk_secret.raw, ctx, + derived_key, derived_keysize); +out_put_key: + key_put(key); + return err; +} + static const struct { const char *cipher_str; int keysize; diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index c4c6bf2c390e..7ca8a44fc984 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -84,6 +84,11 @@ static inline int fscrypt_inherit_context(struct inode *parent, } /* keyinfo.c */ +static inline int fscrypt_ioctl_add_key(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_get_encryption_info(struct inode *inode) { return -EOPNOTSUPP; diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 2db5e9706f60..313943214d57 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -42,6 +42,7 @@ extern int fscrypt_has_permitted_context(struct inode *, struct inode *); extern int fscrypt_inherit_context(struct inode *, struct inode *, void *, bool); /* keyinfo.c */ +extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg); extern int fscrypt_get_encryption_info(struct inode *); extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 26c381a40279..aebe5d84d091 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -34,22 +34,43 @@ struct fscrypt_policy { __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; -#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) -#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) -#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) - -/* Parameters for passing an encryption key into the kernel keyring */ +/* + * Process-subscribed "logon" key description prefix and payload format. + * Deprecated; prefer FS_IOC_ADD_ENCRYPTION_KEY instead. + */ #define FSCRYPT_KEY_DESC_PREFIX "fscrypt:" -#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8 - -/* Structure that userspace passes to the kernel keyring */ -#define FSCRYPT_MAX_KEY_SIZE 64 - +#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8 +#define FSCRYPT_MAX_KEY_SIZE 64 struct fscrypt_key { __u32 mode; __u8 raw[FSCRYPT_MAX_KEY_SIZE]; __u32 size; }; + +struct fscrypt_key_specifier { + __u32 type; +#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 + __u32 reserved; + union { + __u8 max_specifier[32]; + __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + }; +}; + +/* Struct passed to FS_IOC_ADD_ENCRYPTION_KEY */ +struct fscrypt_add_key_args { + __u32 raw_size; + __u32 reserved1; + __u64 reserved2[2]; + struct fscrypt_key_specifier key_spec; + __u8 raw[]; +}; + +#define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW( 'f', 20, __u8[16]) +#define FS_IOC_GET_ENCRYPTION_POLICY _IOW( 'f', 21, struct fscrypt_policy) +#define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 22, struct fscrypt_add_key_args) + /**********************************************************************/ /* old names; don't add anything new here! */ From patchwork Mon Oct 23 21:40:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829676 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pf4Lf4vx"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKX3nvyz9sPr for ; Tue, 24 Oct 2017 08:44:16 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932366AbdJWVoM (ORCPT ); Mon, 23 Oct 2017 17:44:12 -0400 Received: from mail-io0-f195.google.com ([209.85.223.195]:46172 "EHLO mail-io0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751640AbdJWVmU (ORCPT ); Mon, 23 Oct 2017 17:42:20 -0400 Received: by mail-io0-f195.google.com with SMTP id 101so21727138ioj.3; Mon, 23 Oct 2017 14:42:19 -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=/i3mqgW8YjPejX5sRMdtUKZ5bdvjOqMKhpBbmcCFtsA=; b=pf4Lf4vxCMPSkzQdepfakYfLfqtb5OEmzeI/9osFT+YqtBhj8birEHfVCSfLQCvoWt tUA8gn8lU3bBt096uSaBuGMuJpDup/RFZsx74g+c+0Y/L/aCd2CsFLY+4XFAV8RUC1P9 dxX65J7cO9VBg7dlkdUd6SsIBo1krt1IRQXM1Zz0n4TDkuSxE6uxUwb0dOcj5HrXRkzX llxi1F66r49bF081Kw8+OJK4ri22ZVboTJeXkqbiNX5r/qAnhxRmamVYgF3Ooj8k1BTb sKyh9Nm9c9RVeT2yufgmun6KHb4+OId4xFWF8hfB8+Nf0ll05DeKO00gzNdaPimPhzve ePDw== 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=/i3mqgW8YjPejX5sRMdtUKZ5bdvjOqMKhpBbmcCFtsA=; b=Z1Q5qwjwkXO9EpM+2UEkPxevAgiJqf+l4ooKzLI1XHNcWWZD81mPoPkDVcMf+zQ23c lxe+Lx406od/kvINbogwEsBkRSA13H+KTIO0BiYepIv7eDyFgnEJukvBoEgVb3XIU0W+ 3xPtIfmkNF65yV9/gdK/7hUjmw7GEyKF5UDAkoB4iu8ivspLgTso6ktg0Z0QpEKY1ZMR heIvWNaEvXMMCy7oCF2OttEjsyiNy4Qf9zgRVvqf8hvgbJ8+adyjarIezfbdgHxieAVW qo2UB/J6xuZ25afXg2EjnbwiJK0kqWnpUpZmLTXWNtPXwbgAyiNkcsMqQd3SX732ioUc 4rxw== X-Gm-Message-State: AMCzsaViWiyW/295uYeRzi18qDi3b1Pmr+P5Iyjp5LVTjrA6z3JmXAkj nYxHnlC/8WFZxLoBNI0xHA+MTa9h X-Google-Smtp-Source: ABhQp+Qfuw9qHCHWk4gvlHTKuusnzfmAaDBxjbAAU+w4cSF7Nd4Z3rDDOLovjN76TP4IDsu5gsZd/w== X-Received: by 10.107.138.222 with SMTP id c91mr10432868ioj.246.1508794938985; Mon, 23 Oct 2017 14:42:18 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:18 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 07/25] fs/inode.c: export inode_lru_list_del() Date: Mon, 23 Oct 2017 14:40:40 -0700 Message-Id: <20171023214058.128121-8-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers When a filesystem encryption key is removed, we need all files which had been "unlocked" (had ->i_crypt_info set up) with it to appear "locked" again. This is most easily done by evicting the inodes. This can currently be done using 'echo 2 > /proc/sys/vm/drop_caches'; however, that is overkill and not usable by non-root users. In preparation for allowing fs/crypto/ to evict just the needed inodes, export inode_lru_list_del() to modules. Signed-off-by: Eric Biggers Reviewed-by: Michael Halcrow --- fs/inode.c | 5 ++--- include/linux/fs.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index d1e35b53bb23..30ce98956801 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -420,13 +420,12 @@ void inode_add_lru(struct inode *inode) inode_lru_list_add(inode); } - -static void inode_lru_list_del(struct inode *inode) +void inode_lru_list_del(struct inode *inode) { - if (list_lru_del(&inode->i_sb->s_inode_lru, &inode->i_lru)) this_cpu_dec(nr_unused); } +EXPORT_SYMBOL_GPL(inode_lru_list_del); /** * inode_sb_list_add - add inode to the superblock list of inodes diff --git a/include/linux/fs.h b/include/linux/fs.h index 8cfb0877d32c..2833ace2f01d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2879,6 +2879,7 @@ static inline void lockdep_annotate_inode_mutex_key(struct inode *inode) { }; extern void unlock_new_inode(struct inode *); extern unsigned int get_next_ino(void); extern void evict_inodes(struct super_block *sb); +extern void inode_lru_list_del(struct inode *inode); extern void __iget(struct inode * inode); extern void iget_failed(struct inode *); From patchwork Mon Oct 23 21:40:41 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829674 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="uInkygRH"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKQ54H8z9sPt for ; Tue, 24 Oct 2017 08:44:10 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932359AbdJWVoJ (ORCPT ); Mon, 23 Oct 2017 17:44:09 -0400 Received: from mail-it0-f67.google.com ([209.85.214.67]:43302 "EHLO mail-it0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751661AbdJWVmV (ORCPT ); Mon, 23 Oct 2017 17:42:21 -0400 Received: by mail-it0-f67.google.com with SMTP id k70so6822015itk.0; Mon, 23 Oct 2017 14:42:21 -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=wiAoc0JHIPz4HAo1BFCpnFyXTNkBIyNbTooXOraiqBE=; b=uInkygRHFd34bCPKGXqHDVXFaWKgpkAJENWTj8+6DxI/CVmkZ2vUM9R9cxo8lz97t7 2YMFfSN+elcoCimm90w4Lxlon1wjrCRLfYcYZpZLzaNcJgxaIR+iUUVAjN32o81OtQaV Z/zCIs5dwVcl973xb8pbystw/6rm74XgEmLnk24EfGRgYJwAcud6sqTI/7/DjrROCyCA k1OYThU0y5hyJkQlelNsg6fBGw/qNIuHEIuuLIB94gDtDvW2ZVs2sp+TdqEQqqIKMWOS DhZMyFr1ni05VcDwKjPGEPD0kQd11FAkoxuiMtyMl6nmA4ztcNb9q+XPIjhcvyTtvPxm R+fA== 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=wiAoc0JHIPz4HAo1BFCpnFyXTNkBIyNbTooXOraiqBE=; b=LKndq1JDXlyMEeTr4djzDJB8w4/C5jHUW4lNccykg2OKXyt7OX1d2IuygIO2YBpzly QNhfElxsunoI8JlDgH99sx16eazweJT3Geb7/esuwEhaDJpxCbVnJy69JQet0hvE69R1 We5Sxn5dsPLEgxBRW/LmTWhLEGSGOs6dZ1KQFU8GOWZn/vE7v6+r/y5yg3cYsMyhYfU7 hvjFkPJ4EpNzAK8TVHj6y9Bqo7rjoNXCaVrI5MG37LPzS2JlmiHjXEYi9OkIGldWERQn mzMs0Hx2AQRoj6p61uDtmXsnvtYCNNhtGSBwTbMUHTAn4Z+p1d1VtpAFK2sbe/yuAHWj KlJw== X-Gm-Message-State: AMCzsaXog2Fc/mSyFxHw5q+8MnpmULAWgwWv7LfSR0PMRSBuchUeW6vn dLig/BxFZsM3KBTfY96x560dljFg X-Google-Smtp-Source: ABhQp+QklVKqmOxvS5EPAR1bGWPJimKjCOvUruutksE3uobbNpP7koBEHKEQEWTx+iR3pEu0xgHf+A== X-Received: by 10.36.77.131 with SMTP id l125mr10541183itb.7.1508794940291; Mon, 23 Oct 2017 14:42:20 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:19 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 08/25] fs/inode.c: rename and export dispose_list() Date: Mon, 23 Oct 2017 14:40:41 -0700 Message-Id: <20171023214058.128121-9-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers When a filesystem encryption key is removed, we need all files which had been "unlocked" (had ->i_crypt_info set up) with it to appear "locked" again. This is most easily done by evicting the inodes. This can currently be done using 'echo 2 > /proc/sys/vm/drop_caches'; however, that is overkill and not usable by non-root users. In preparation for allowing fs/crypto/ to evict just the needed inodes, export dispose_list() to modules. For clarity also rename it to evict_inode_list(). Signed-off-by: Eric Biggers --- fs/inode.c | 19 ++++++++++--------- include/linux/fs.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 30ce98956801..fe47930835c0 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -570,13 +570,13 @@ static void evict(struct inode *inode) } /* - * dispose_list - dispose of the contents of a local list - * @head: the head of the list to free + * evict_inode_list - evict each inode in a local list of inodes + * @head: the head of the list * - * Dispose-list gets a local list with local inodes in it, so it doesn't + * This gets a local list with local inodes in it, so it doesn't * need to worry about list corruption and SMP locks. */ -static void dispose_list(struct list_head *head) +void evict_inode_list(struct list_head *head) { while (!list_empty(head)) { struct inode *inode; @@ -588,6 +588,7 @@ static void dispose_list(struct list_head *head) cond_resched(); } } +EXPORT_SYMBOL_GPL(evict_inode_list); /** * evict_inodes - evict all evictable inodes for a superblock @@ -628,13 +629,13 @@ void evict_inodes(struct super_block *sb) if (need_resched()) { spin_unlock(&sb->s_inode_list_lock); cond_resched(); - dispose_list(&dispose); + evict_inode_list(&dispose); goto again; } } spin_unlock(&sb->s_inode_list_lock); - dispose_list(&dispose); + evict_inode_list(&dispose); } EXPORT_SYMBOL_GPL(evict_inodes); @@ -679,7 +680,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) } spin_unlock(&sb->s_inode_list_lock); - dispose_list(&dispose); + evict_inode_list(&dispose); return busy; } @@ -763,7 +764,7 @@ static enum lru_status inode_lru_isolate(struct list_head *item, * Walk the superblock inode LRU for freeable inodes and attempt to free them. * This is called from the superblock shrinker function with a number of inodes * to trim from the LRU. Inodes to be freed are moved to a temporary list and - * then are freed outside inode_lock by dispose_list(). + * then are freed outside inode_lock by evict_inode_list(). */ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc) { @@ -772,7 +773,7 @@ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc) freed = list_lru_shrink_walk(&sb->s_inode_lru, sc, inode_lru_isolate, &freeable); - dispose_list(&freeable); + evict_inode_list(&freeable); return freed; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 2833ace2f01d..e0a8dae5f9dc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2880,6 +2880,7 @@ extern void unlock_new_inode(struct inode *); extern unsigned int get_next_ino(void); extern void evict_inodes(struct super_block *sb); extern void inode_lru_list_del(struct inode *inode); +extern void evict_inode_list(struct list_head *head); extern void __iget(struct inode * inode); extern void iget_failed(struct inode *); From patchwork Mon Oct 23 21:40:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829671 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="rZT3CF0V"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKH4DbMz9sPt for ; Tue, 24 Oct 2017 08:44:03 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932184AbdJWVn7 (ORCPT ); Mon, 23 Oct 2017 17:43:59 -0400 Received: from mail-it0-f47.google.com ([209.85.214.47]:47443 "EHLO mail-it0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751667AbdJWVmW (ORCPT ); Mon, 23 Oct 2017 17:42:22 -0400 Received: by mail-it0-f47.google.com with SMTP id p138so7663534itp.2; Mon, 23 Oct 2017 14:42:22 -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=MwU8NLXH/25/d6bIsCheKX9gJkr397GYZ6MiKkLdbzw=; b=rZT3CF0VFfVKR0XZA5ggZMYq2xtwDj92msG2b5uHreLbWb6eLQOp4ubM4OBMtDyiGP UxX80cJWNu9l08zAvlQoOOGe42eTrDYaueZ1BLMFerF2L1T8Hn3itdW3kV+tgAF/J1Iy bCpd41E0HhVzKbOankK42Np3dgy5aH6HjjdqZFi+MQEAvTMjeORcuE9nLKmwHDP3Satm zAJ/WrYkaQ0e6k2Z7+DtFez3LtG8q7Yd8i/kpsnlPg02S1MewJQTg5HrB7XWdHAUd2Pp HKHqAEBpLOs2vCW8d3dOVTbONvmKDKYWkroY5i2viH2rdhDYzFJz6lv7iDqzyYeTb1yA Epsw== 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=MwU8NLXH/25/d6bIsCheKX9gJkr397GYZ6MiKkLdbzw=; b=pXoq+9a+9voVvf98wSjeeZhduyJECw4RnlzvCSaLGQiETYWMJd3d5YBobxy83UEjqA ueEd+6zO6zcjw/n/veqBrwZYfHeKNOA9Ti8VHxCY9K3VUMEOKTWeoSDYefa9yk1A2dhl /9M4VSsDP63gJwdLJCzsxjLUneQ6/2CTdnz4e4ygGFr3rzkBHU0PzP4PyjZ8p3RAB2U6 rcyUAW/bjwIf1TpW5tczyzSly0I2fh+LBB/rcK9WAi6AMw6AgXtOEAP+m2AJwtrAE4Xy xSOKBsrVavJmzJm4Ygxm7lPz+eFrPX06pFRH7i/DOzjR8xqUcZpPPVKIgX4qZ5UHHVYv ct6A== X-Gm-Message-State: AMCzsaWHKmjKz5KDopR6dpqtdO16xsbysM1xXfl6+KWQWthxwGvOhksO 41ogZpeIEB5FNDYorhSEXdz4c/GV X-Google-Smtp-Source: ABhQp+RJH5bdwhb+eDMUqLDVaXUw+70J7zLZGdOQU2/7Fgf/3gFqtLdScknrr1jnLT4jh9laeuBtew== X-Received: by 10.36.244.69 with SMTP id u5mr11939455iti.67.1508794941530; Mon, 23 Oct 2017 14:42:21 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:21 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 09/25] fs/dcache.c: add shrink_dcache_inode() Date: Mon, 23 Oct 2017 14:40:42 -0700 Message-Id: <20171023214058.128121-10-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers When a filesystem encryption key is removed, we need all files which had been "unlocked" (had ->i_crypt_info set up) with it to appear "locked" again. This is most easily done by evicting the inodes. This can currently be done using 'echo 2 > /proc/sys/vm/drop_caches'; however, that is overkill and not usable by non-root users. To evict just the needed inodes we also need the ability to evict those inodes' dentries, since an inode is pinned by its dentries. Therefore, add a function shrink_dcache_inode() which iterates through an inode's dentries and evicts any unused ones as well as any unused descendants (since there may be negative dentries pinning the inode's dentries). Signed-off-by: Eric Biggers --- fs/dcache.c | 33 +++++++++++++++++++++++++++++++++ include/linux/dcache.h | 1 + 2 files changed, 34 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index f90141387f01..455540e889f8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1456,6 +1456,39 @@ void shrink_dcache_parent(struct dentry *parent) } EXPORT_SYMBOL(shrink_dcache_parent); +/** + * shrink_dcache_inode - prune dcache for inode + * @inode: inode to prune + * + * Evict all unused aliases of the specified inode from the dcache. This is + * intended to be used when trying to evict a specific inode, since inodes are + * pinned by their dentries. We also have to descend to ->d_subdirs for each + * alias, since aliases may be pinned by negative child dentries. + */ +void shrink_dcache_inode(struct inode *inode) +{ + for (;;) { + struct select_data data; + struct dentry *dentry; + + INIT_LIST_HEAD(&data.dispose); + data.start = NULL; + data.found = 0; + + spin_lock(&inode->i_lock); + hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) + d_walk(dentry, &data, select_collect, NULL); + spin_unlock(&inode->i_lock); + + if (!data.found) + break; + + shrink_dentry_list(&data.dispose); + cond_resched(); + } +} +EXPORT_SYMBOL(shrink_dcache_inode); + static enum d_walk_ret umount_check(void *_data, struct dentry *dentry) { /* it has busy descendents; complain about those instead */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index ed1a7cf6923a..fb08199d67d5 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -245,6 +245,7 @@ extern struct dentry * d_obtain_alias(struct inode *); extern struct dentry * d_obtain_root(struct inode *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); +extern void shrink_dcache_inode(struct inode *); extern void shrink_dcache_for_umount(struct super_block *); extern void d_invalidate(struct dentry *); From patchwork Mon Oct 23 21:40:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829655 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="PuXDLx90"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHk57WMz9sRg for ; Tue, 24 Oct 2017 08:42:42 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932240AbdJWVmk (ORCPT ); Mon, 23 Oct 2017 17:42:40 -0400 Received: from mail-it0-f67.google.com ([209.85.214.67]:52974 "EHLO mail-it0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932141AbdJWVmZ (ORCPT ); Mon, 23 Oct 2017 17:42:25 -0400 Received: by mail-it0-f67.google.com with SMTP id j140so7715070itj.1; Mon, 23 Oct 2017 14:42:24 -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=rVh1lG19FDPK0jBQFfvJ5uWV+auXzHbLRl7foWj3oRQ=; b=PuXDLx90jyyEm8vgBfx/yXIYxfkq4BwIDBgv6jbvtLG9VRU2C3lktgWqZa5IreR3bY ath7kTN0+JNokzqUU091ExTD0+3/vguAEJep73yboB8idwoFporwgcCH19GgmGPAoQFg 9Df7N1TOXzquzBgzc/LmkV3jWjNMjkpIzTvASAwBw3om6XGHFQY6AZ8NiAMtNNQgKbO5 beQQ3lP78B/fTnJ9THNs90RjuTg3J2bqeQeWgm7+7U7gLfw1vP2a6bpkYBoAXG+ZV8ZH Ij3hnSTBCZ4U8FWiiEv2L7lbIeX9YR7p/Voav0e8xBtiRcYfPMiU4GY6hzEzEto3YqFj 9Jzw== 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=rVh1lG19FDPK0jBQFfvJ5uWV+auXzHbLRl7foWj3oRQ=; b=gg1m8DfPymZe5zEYV1g2CnlegGyFcEa1aSrPv1jN/AW813SWvF//+dedXQulaqKylS JbvIXJfRkLNpF5cgo7t3QCB7KVACRFB2D2iqzW2x2ZaD2Ww9GUIU2EHreerkWHsnPxiS sPQOEKjpQ3szghBYNvhmW+h5YDcp0KMAnTF25JkeEENtOouA9FtBBvo7Qi8o50UEvkiA WZlU1Hn7lBmZka8pJfceaxLdXcpiKXn96grFoH4eG0lCoLGzozxHhrfIMzftiKxxtt1r FKAuWbO5B2l9LyngEn7ez6nqOEUut0adtVzKSo2G+V59qZ+p7lHBSVgzbJXkyGDMVnq6 Wmtw== X-Gm-Message-State: AMCzsaWdgAjvf1kRtXUouVIAmyGpPoEdRM+CtLHKAOeQreaazDNKWbsW 05Z7wsK3IBZ32YQKYbnWyXrWhdFf X-Google-Smtp-Source: ABhQp+T82uRWMcwicRLxG2Bd6yZ6qjtW2RoZIKbe9en3lFk4fhWAB+ePqoPWvgstuBrY0tNu1QhITw== X-Received: by 10.36.238.73 with SMTP id b70mr9937458iti.127.1508794942923; Mon, 23 Oct 2017 14:42:22 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:22 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 10/25] fscrypt: add FS_IOC_REMOVE_ENCRYPTION_KEY ioctl Date: Mon, 23 Oct 2017 14:40:43 -0700 Message-Id: <20171023214058.128121-11-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Problem ~~~~~~~ Many filesystem encryption users want the ability to remove encryption keys, causing the corresponding encrypted directories to appear "locked" (presented in ciphertext form) again. Moreover, users want removing an encryption key to *really* remove it, in the sense that the removed keys cannot be recovered even if kernel memory is compromised, e.g. by the exploit of a kernel security vulnerability or by a physical attack. This is desirable after a user logs out of the system, for example. In many cases users even already assume this to be the case and are surprised to hear when it's not. It is *not* sufficient to simply unlink the master key from the keyring (or to revoke or invalidate it), since files are encrypted with per-file keys instead of with the master keys directly. Therefore, to really remove a key we must also remove the per-file keys, e.g. by evicting the corresponding inodes from the inode cache. This also would have the benefit of making encrypted files appear "locked" again. Currently the workaround is to run: sync echo 2 > /proc/sys/vm/drop_caches This is a very bad solution because it evicts all not-in-use inodes in the system rather than just the inodes associated with the key being removed. Moreover, it requires root privileges, so non-root users cannot lock their encrypted directories. Finally, the drop_caches sysctl was originally meant for debugging purposes only. Nevertheless, the largest users of filesystem encryption (Android and Chromium OS) actually want this capability badly enough that they are actually using the drop_caches workaround. Similarly, the drop_caches workaround is also used in the PAM module provided by the fscrypt userspace tool (https://github.com/google/fscrypt). Needless to say, this is causing significant performance problems due to inodes for unencrypted system files being evicted. So a real solution is needed. Solution ~~~~~~~~ To properly solve this problem, we need an API which removes and wipes the given master key, *and* removes and and wipes the corresponding per-file keys. This requires tracking which inodes have been "unlocked" using each master key. Originally that was not possible because the kernel didn't actually have a centralized notion of what a master key even was. But now that we have the filesystem-level keyring ->s_master_keys it is finally possible. Add this API as a new ioctl, FS_IOC_REMOVE_ENCRYPTION_KEY. It is the counterpart of FS_IOC_ADD_ENCRYPTION_KEY. FS_IOC_REMOVE_ENCRYPTION_KEY first wipes the master key's secret from memory. Then, it syncs the filesystem and tries to evict the list of inodes that had been "unlocked" with the key. Evicting the inodes has several effects, including: - The actual keys used to encrypt the data (in ->i_crypt_info->ci_ctfm) are wiped from memory. Thus, they can no longer be recovered, even if kernel memory is later compromised. - The encrypted files and directories once again appear "locked", i.e. in ciphertext or in "encrypted" form. This is highly desirable from a user interface perspective. It can also be desirable from a security perspective (although sometimes for the wrong reasons!). - The pagecache pages are freed, which allows the plaintext file contents to be overwritten in memory later as the system continues running. Currently we do not actually wipe the pages on free, nor does the kernel more generally wipe memory on free either. Thus, for now we tolerate that an attacker who later gains access to kernel memory may be able to see portions of file contents and file names in plaintext in unallocated memory. Security-conscious users who do not mind a performance hit may ameliorate this by enabling page poisoning. Of course, some inodes may still be in use when a master key is removed, and we cannot simply revoke random file descriptors, mmap's, etc. The approach we take is to skip in-use inodes, and notify userspace by returning -EBUSY if any inodes could not be evicted. Still, even in this case the master key secret is removed, so no more files can be unlocked with it. Moreover, most of the inodes should still be evicted as well. Userspace can then retry the ioctl later to evict the remaining inodes. Alternatively, if userspace adds the key again, then the refreshed secret will be associated with the existing list of inodes so that they are correctly tracked for future key removals. For now, FS_IOC_REMOVE_ENCRYPTION_KEY has to be restricted to privileged users only, just like FS_IOC_ADD_ENCRYPTION_KEY. This is sufficient for use cases where all encryption keys are managed by a privileged process, e.g. as is the case on Android and Chromium OS. But in the more general case, non-root users need to be able to both unlock *and* lock their own encrypted directories. As it turns out, we will indeed be able to support this through these ioctls, but non-root use will need to be tied to the use of a new encryption policy version which identifies the master key using a cryptographic hash. (See later patches.) Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 18 ++- fs/crypto/keyinfo.c | 347 ++++++++++++++++++++++++++++++++++++++-- fs/crypto/policy.c | 5 +- include/linux/fscrypt_notsupp.h | 6 + include/linux/fscrypt_supp.h | 1 + include/uapi/linux/fscrypt.h | 7 + 6 files changed, 367 insertions(+), 17 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index b2fad12eeedb..2fdc4e5c0771 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -61,7 +61,23 @@ struct fscrypt_info { u8 ci_flags; struct crypto_skcipher *ci_ctfm; struct crypto_cipher *ci_essiv_tfm; - u8 ci_master_key[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + u8 ci_master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + + /* + * The master key with which this inode was unlocked (decrypted). This + * will be NULL if the master key was found in a process-subscribed + * keyring rather than in the filesystem-level keyring. + */ + struct key *ci_master_key; + + /* Link in list of inodes that were unlocked with the master key */ + struct list_head ci_master_key_link; + + /* + * Back-pointer to the inode, needed during key removal. Only set when + * ->ci_master_key is set. + */ + struct inode *ci_inode; }; typedef enum { diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 3f1cb8bbc1e5..dc2697cf9114 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -40,11 +40,35 @@ struct fscrypt_master_key_secret { */ struct fscrypt_master_key { - /* The secret key material */ + /* + * The secret key material. After FS_IOC_REMOVE_ENCRYPTION_KEY is + * executed, this is wiped and no new inodes can be unlocked with this + * key; however, there may still be inodes in ->mk_decrypted_inodes + * which could not be evicted. As long as some inodes still remain, + * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or + * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. + * + * Locking: protected by key->sem. + */ struct fscrypt_master_key_secret mk_secret; /* Arbitrary key descriptor which was assigned by userspace */ struct fscrypt_key_specifier mk_spec; + + /* + * Length of ->mk_decrypted_inodes, plus one if mk_secret is present. + * Once this goes to 0, the master key is removed from ->s_master_keys. + * This struct will continue to live as long as the 'struct key' whose + * payload it is, but we won't let this reference count rise again. + */ + refcount_t mk_refcount; + + /* + * List of inodes that were unlocked using this key. This allows the + * inodes to be evicted efficiently if the key is removed. + */ + struct list_head mk_decrypted_inodes; + spinlock_t mk_decrypted_inodes_lock; }; static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec) @@ -63,6 +87,12 @@ static inline bool valid_key_spec(const struct fscrypt_key_specifier *spec) return master_key_spec_len(spec) != 0; } +static bool +is_master_key_secret_present(const struct fscrypt_master_key_secret *secret) +{ + return secret->size != 0; +} + static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret) { memzero_explicit(secret, sizeof(*secret)); @@ -96,6 +126,13 @@ static void fscrypt_key_destroy(struct key *key) static void fscrypt_key_describe(const struct key *key, struct seq_file *m) { seq_puts(m, key->description); + + if (key_is_positive(key)) { + struct fscrypt_master_key *mk = key->payload.data[0]; + + if (!is_master_key_secret_present(&mk->mk_secret)) + seq_puts(m, ": secret removed"); + } } /* @@ -122,7 +159,8 @@ static struct key *search_fscrypt_keyring(struct key *keyring, keyref = keyring_search(make_key_ref(keyring, 1), type, description); if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) == -EAGAIN) + if (PTR_ERR(keyref) == -EAGAIN || + PTR_ERR(keyref) == -EKEYREVOKED) keyref = ERR_PTR(-ENOKEY); return ERR_CAST(keyref); } @@ -186,6 +224,10 @@ allocate_master_key(struct fscrypt_master_key_secret *secret, move_master_key_secret(&mk->mk_secret, secret); + refcount_set(&mk->mk_refcount, 1); /* secret is present */ + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); + spin_lock_init(&mk->mk_decrypted_inodes_lock); + format_mk_description(description, mk_spec); key = key_alloc(&key_type_fscrypt_mk, description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), @@ -243,6 +285,7 @@ static int add_master_key(struct super_block *sb, static DEFINE_MUTEX(fscrypt_add_key_mutex); mutex_lock(&fscrypt_add_key_mutex); /* serialize find + link */ +retry: key = find_master_key(sb, mk_spec); if (IS_ERR(key)) { if (key != ERR_PTR(-ENOKEY)) { @@ -259,6 +302,33 @@ static int add_master_key(struct super_block *sb, err = add_to_filesystem_keyring(sb, key); if (err) goto out_put_key; + } else { + struct fscrypt_master_key *mk = key->payload.data[0]; + bool rekey; + + /* Found the key in ->s_master_keys */ + + down_write(&key->sem); + + /* + * Take a reference if we'll be re-adding ->mk_secret. If we + * couldn't take a reference, then the key is being removed from + * ->s_master_keys and can no longer be used. So invalidate the + * key (someone else is doing that too, but they might be + * slower) and retry searching ->s_master_keys. + */ + rekey = !is_master_key_secret_present(&mk->mk_secret); + if (rekey && !refcount_inc_not_zero(&mk->mk_refcount)) { + up_write(&key->sem); + key_invalidate(key); + key_put(key); + goto retry; + } + + /* Re-add the secret key material if needed */ + if (rekey) + move_master_key_secret(&mk->mk_secret, secret); + up_write(&key->sem); } err = 0; out_put_key: @@ -270,7 +340,8 @@ static int add_master_key(struct super_block *sb, /* * Add a master encryption key to the filesystem, causing all files which were - * encrypted with it to appear "unlocked" (decrypted) when accessed. + * encrypted with it to appear "unlocked" (decrypted) when accessed. The key + * can be removed later by FS_IOC_REMOVE_ENCRYPTION_KEY. */ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) { @@ -310,6 +381,191 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) } EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); +static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk) +{ + struct fscrypt_info *ci; + struct inode *inode; + struct inode *toput_inode = NULL; + + spin_lock(&mk->mk_decrypted_inodes_lock); + + list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) { + inode = ci->ci_inode; + spin_lock(&inode->i_lock); + if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) { + spin_unlock(&inode->i_lock); + continue; + } + __iget(inode); + spin_unlock(&inode->i_lock); + spin_unlock(&mk->mk_decrypted_inodes_lock); + + shrink_dcache_inode(inode); + iput(toput_inode); + toput_inode = inode; + + spin_lock(&mk->mk_decrypted_inodes_lock); + } + + spin_unlock(&mk->mk_decrypted_inodes_lock); + iput(toput_inode); +} + +static int evict_decrypted_inodes(struct fscrypt_master_key *mk) +{ + struct fscrypt_info *ci; + struct inode *inode; + LIST_HEAD(dispose); + unsigned long num_busy = 0; + unsigned long busy_ino; + + spin_lock(&mk->mk_decrypted_inodes_lock); + + list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) { + inode = ci->ci_inode; + spin_lock(&inode->i_lock); + + if (inode->i_state & (I_FREEING | I_WILL_FREE)) + goto next; + + if (atomic_read(&inode->i_count) || + (inode->i_state & ~I_REFERENCED)) { + num_busy++; + busy_ino = inode->i_ino; + goto next; + } + + inode->i_state |= I_FREEING; + inode_lru_list_del(inode); + list_add(&inode->i_lru, &dispose); +next: + spin_unlock(&inode->i_lock); + } + + spin_unlock(&mk->mk_decrypted_inodes_lock); + + evict_inode_list(&dispose); + + if (unlikely(num_busy)) { + pr_warn_ratelimited("fscrypt: %lu inodes still busy after removing key with description %*phN (%sino: %lu)\n", + num_busy, master_key_spec_len(&mk->mk_spec), + mk->mk_spec.max_specifier, + (num_busy > 1 ? "example " : ""), busy_ino); + return -EBUSY; + } + + return 0; +} + +static int try_to_lock_encrypted_files(struct super_block *sb, + struct fscrypt_master_key *mk) +{ + int err1; + int err2; + + /* + * An inode can't be evicted while it still has dirty pages, or while + * the inode itself is still dirty. Thus, we first have to clean all + * the inodes in ->mk_decrypted_inodes. + * + * Just do it the easy way: call sync_filesystem(). It's overkill, but + * it works, and it's more important to minimize the amount of caches we + * drop than the amount of data we sync. Also, unprivileged users can + * already call sync_filesystem() via sys_syncfs() or sys_sync(). + */ + down_read(&sb->s_umount); + err1 = sync_filesystem(sb); + up_read(&sb->s_umount); + + /* + * Inodes are pinned by their dentries, so we have to evict the dentries + * first. We could potentially just call shrink_dcache_sb() here, but + * that would be overkill, and an unprivileged user shouldn't be able to + * evict all dentries for the entire filesystem. Instead, go through + * the inodes' alias lists and try to evict each dentry. + */ + evict_dentries_for_decrypted_inodes(mk); + + /* + * Finally, iterate through ->mk_decrypted_inodes and evict as many + * inodes as we can. Similarly, we could potentially just call + * invalidate_inodes() here, but that would be overkill, and an + * unprivileged user shouldn't be able to evict all inodes for the + * entire filesystem. + * + * Note that ideally, we wouldn't really evict the inodes, but rather + * just free their ->i_crypt_info and pagecache. But eviction is *much* + * easier to correctly implement without causing use-after-free bugs. + */ + err2 = evict_decrypted_inodes(mk); + + return err1 ?: err2; +} + +/* + * Try to remove an fscrypt master encryption key. + * + * First we wipe the actual master key secret from memory, so that no more + * inodes can be unlocked with it. Then, we try to evict all cached inodes that + * had been unlocked using the key. Since this can fail for in-use inodes, this + * is expected to be used in cooperation with userspace ensuring that none of + * the files are still open. + * + * If, nevertheless, some inodes could not be evicted, we return -EBUSY + * (although we still evicted as many inodes as possible) and keep the 'struct + * key' and the 'struct fscrypt_master_key' around to keep track of the list of + * remaining inodes. Userspace can then retry the ioctl later to retry evicting + * the remaining inodes, or alternatively can add the secret key again. + * + * Note that even though we wipe the encryption *keys* from memory, decrypted + * data can likely still be found in memory, e.g. in pagecache pages that have + * been freed. Wiping such data is currently out of scope, short of users who + * may choose to enable page and slab poisoning systemwide. + */ +int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct fscrypt_remove_key_args arg; + struct key *key; + struct fscrypt_master_key *mk; + int err; + bool dead; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved))) + return -EINVAL; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + key = find_master_key(sb, &arg.key_spec); + if (IS_ERR(key)) + return PTR_ERR(key); + mk = key->payload.data[0]; + + down_write(&key->sem); + dead = false; + if (is_master_key_secret_present(&mk->mk_secret)) { + wipe_master_key_secret(&mk->mk_secret); + dead = refcount_dec_and_test(&mk->mk_refcount); + } + up_write(&key->sem); + if (dead) { + key_invalidate(key); + err = 0; + } else { + err = try_to_lock_encrypted_files(sb, mk); + } + key_put(key); + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key); + static void derive_crypt_complete(struct crypto_async_request *req, int rc) { struct fscrypt_completion_result *ecr = req->data; @@ -455,10 +711,20 @@ static int find_and_derive_key_legacy(const struct inode *inode, return err; } -/* Find the master key, then derive the inode's actual encryption key */ +/* + * Find the master key, then derive the inode's actual encryption key. + * + * If the master key is found in the filesystem-level keyring, then the + * corresponding 'struct key' is returned read-locked in *master_key_ret. This + * is needed because we need to hold the semaphore until we link the new + * fscrypt_info into ->mk_decrypted_inodes, but in the case where multiple tasks + * are racing to set up an inode's ->i_crypt_info, only the winner should link + * its fscrypt_info into ->mk_decrypted_inodes. + */ static int find_and_derive_key(const struct inode *inode, const struct fscrypt_context *ctx, - u8 *derived_key, unsigned int derived_keysize) + u8 *derived_key, unsigned int derived_keysize, + struct key **master_key_ret) { struct key *key; struct fscrypt_master_key *mk; @@ -481,6 +747,13 @@ static int find_and_derive_key(const struct inode *inode, derived_keysize); } mk = key->payload.data[0]; + down_read(&key->sem); + + /* Has the secret been removed using FS_IOC_REMOVE_ENCRYPTION_KEY? */ + if (!is_master_key_secret_present(&mk->mk_secret)) { + err = -ENOKEY; + goto out_release_key; + } /* * Require that the master key be at least as long as the derived key. @@ -493,12 +766,19 @@ static int find_and_derive_key(const struct inode *inode, key->description, mk->mk_secret.size, derived_keysize); err = -ENOKEY; - goto out_put_key; + goto out_release_key; } err = derive_key_aes(mk->mk_secret.raw, ctx, derived_key, derived_keysize); -out_put_key: + if (err) + goto out_release_key; + + *master_key_ret = key; + return 0; + +out_release_key: + up_read(&key->sem); key_put(key); return err; } @@ -542,11 +822,32 @@ static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode, static void put_crypt_info(struct fscrypt_info *ci) { + struct key *key; + if (!ci) return; crypto_free_skcipher(ci->ci_ctfm); crypto_free_cipher(ci->ci_essiv_tfm); + key = ci->ci_master_key; + if (key) { + struct fscrypt_master_key *mk = key->payload.data[0]; + + /* + * Remove this inode from the list of inodes that were unlocked + * with the master key. + * + * In addition, if we're removing the last inode from a key that + * already had its secret removed, invalidate the key so that it + * gets removed from ->s_master_keys. + */ + spin_lock(&mk->mk_decrypted_inodes_lock); + list_del(&ci->ci_master_key_link); + spin_unlock(&mk->mk_decrypted_inodes_lock); + if (refcount_dec_and_test(&mk->mk_refcount)) + key_invalidate(key); + key_put(key); + } kmem_cache_free(fscrypt_info_cachep, ci); } @@ -624,6 +925,7 @@ int fscrypt_get_encryption_info(struct inode *inode) const char *cipher_str; unsigned int derived_keysize; u8 *derived_key = NULL; + struct key *master_key = NULL; int res; if (inode->i_crypt_info) @@ -655,17 +957,15 @@ int fscrypt_get_encryption_info(struct inode *inode) if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID) return -EINVAL; - crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS); + crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS); if (!crypt_info) return -ENOMEM; crypt_info->ci_flags = ctx.flags; crypt_info->ci_data_mode = ctx.contents_encryption_mode; crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; - crypt_info->ci_ctfm = NULL; - crypt_info->ci_essiv_tfm = NULL; - memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, - sizeof(crypt_info->ci_master_key)); + memcpy(crypt_info->ci_master_key_descriptor, ctx.master_key_descriptor, + FSCRYPT_KEY_DESCRIPTOR_SIZE); res = determine_cipher_type(crypt_info, inode, &cipher_str, &derived_keysize); @@ -681,7 +981,8 @@ int fscrypt_get_encryption_info(struct inode *inode) if (!derived_key) goto out; - res = find_and_derive_key(inode, &ctx, derived_key, derived_keysize); + res = find_and_derive_key(inode, &ctx, derived_key, derived_keysize, + &master_key); if (res) goto out; @@ -709,9 +1010,27 @@ int fscrypt_get_encryption_info(struct inode *inode) goto out; } } - if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) + + if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) { + if (master_key) { + struct fscrypt_master_key *mk = + master_key->payload.data[0]; + + crypt_info->ci_inode = inode; + refcount_inc(&mk->mk_refcount); + crypt_info->ci_master_key = key_get(master_key); + spin_lock(&mk->mk_decrypted_inodes_lock); + list_add(&crypt_info->ci_master_key_link, + &mk->mk_decrypted_inodes); + spin_unlock(&mk->mk_decrypted_inodes_lock); + } crypt_info = NULL; + } out: + if (master_key) { + up_read(&master_key->sem); + key_put(master_key); + } if (res == -ENOKEY) res = 0; put_crypt_info(crypt_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 19332a6fd52d..a856c8941be6 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -198,7 +198,8 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) child_ci = child->i_crypt_info; if (parent_ci && child_ci) { - return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key, + return memcmp(parent_ci->ci_master_key_descriptor, + child_ci->ci_master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && (parent_ci->ci_data_mode == child_ci->ci_data_mode) && (parent_ci->ci_filename_mode == @@ -253,7 +254,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child, ctx.contents_encryption_mode = ci->ci_data_mode; ctx.filenames_encryption_mode = ci->ci_filename_mode; ctx.flags = ci->ci_flags; - memcpy(ctx.master_key_descriptor, ci->ci_master_key, + memcpy(ctx.master_key_descriptor, ci->ci_master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE); get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE); diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 7ca8a44fc984..92616bfdc294 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -89,6 +89,12 @@ static inline int fscrypt_ioctl_add_key(struct file *filp, void __user *arg) return -EOPNOTSUPP; } +static inline int fscrypt_ioctl_remove_key(struct file *filp, + const void __user *arg) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_get_encryption_info(struct inode *inode) { return -EOPNOTSUPP; diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 313943214d57..620ca4f1bafe 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -43,6 +43,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, void *, bool); /* keyinfo.c */ extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg); +extern int fscrypt_ioctl_remove_key(struct file *filp, const void __user *arg); extern int fscrypt_get_encryption_info(struct inode *); extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index aebe5d84d091..5d02f138668c 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -66,10 +66,17 @@ struct fscrypt_add_key_args { __u8 raw[]; }; +/* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */ +struct fscrypt_remove_key_args { + __u64 reserved[3]; + struct fscrypt_key_specifier key_spec; +}; + #define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy) #define FS_IOC_GET_ENCRYPTION_PWSALT _IOW( 'f', 20, __u8[16]) #define FS_IOC_GET_ENCRYPTION_POLICY _IOW( 'f', 21, struct fscrypt_policy) #define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 22, struct fscrypt_add_key_args) +#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOR( 'f', 23, struct fscrypt_remove_key_args) /**********************************************************************/ From patchwork Mon Oct 23 21:40:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829658 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pLvt1dOG"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJ61JNpz9sPr for ; Tue, 24 Oct 2017 08:43:02 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932219AbdJWVmh (ORCPT ); Mon, 23 Oct 2017 17:42:37 -0400 Received: from mail-it0-f67.google.com ([209.85.214.67]:44226 "EHLO mail-it0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932148AbdJWVmZ (ORCPT ); Mon, 23 Oct 2017 17:42:25 -0400 Received: by mail-it0-f67.google.com with SMTP id n195so6818241itg.1; Mon, 23 Oct 2017 14:42:24 -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=4fk93GRnVqCBDf1H2gOV6iWZyAWe9dS54rgSGmwqAXM=; b=pLvt1dOGDpggKyTDMdVd70KKoDZFtldFssI//VPvuHYNWj3HVjWH3Loqvvgqo2E2dL kH/jlYD58xtuzhB0ACU2q4SDXPjP5tRKNCoENhWSaqH2ZTtdarHl7MHSTWn0IXmtZHFi Tn+/dilPkhNL8e7A+A6+DTBMYr4HRNu/rHqXvqDk2dajZiQEE5p0H9gsWtOpnJ1MTrBh cY9vPkT8Gjkcanuxeuq50w1Km8eF7Ymho9DMFh9EYq2Nhz/GSonCUFIxnyLcsCAph7+f eTsqXgBr4B9z3XN9TnPaZYYoHNA1GjcnVNDpab2qse1TBS1e/ZNmMfIvR8i/6HgYmRHL 8P7g== 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=4fk93GRnVqCBDf1H2gOV6iWZyAWe9dS54rgSGmwqAXM=; b=Wlk4agmB/jI25vg8hcTro3az69jDM+P//hFDorzaPiJ0LSt20KQPoHgiX1FPhGEN3w vKo1C1rtYSPAFo5WnB+NygSVDckhJJ9dQnCNeTRMNw26i4HvwBbBp8zDoshJclfsNwAD 89dDkZkD+Gdh+6ni6QZcwn0ahdoUHWvjrSGdfOKDvydoTUGuvqqHigE/70P8gmxKWbOY 34VhWJNp7Z7cZF+AWS3sYJy3Y9EC5XKm+OE6Ltt+ALoKte9dsDDDEUGXvU/5QTdsOESo yG9aMvrYObVUFIJShb52Z076inx+WlUZ+14/6yq/LTQRa9lwI0UK+/LJ9NFIWpruAZ3y PtFw== X-Gm-Message-State: AMCzsaVFOhaxM+I5eeB0JdjB2h8Io7p3X1+mI0FGOwFyIbukXlXf9N17 +FX85Urz2fPtS7rkl8O9IeTJIM2n X-Google-Smtp-Source: ABhQp+TNQdKeR5heSfEVv/atoiJJ15KMNOPZCI1va3YqPUNGI4lnWa2LHZPjRMIsgM2PpxZrLm6kbw== X-Received: by 10.36.54.210 with SMTP id l201mr11073461itl.0.1508794944157; Mon, 23 Oct 2017 14:42:24 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.22 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:23 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 11/25] fscrypt: add FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl Date: Mon, 23 Oct 2017 14:40:44 -0700 Message-Id: <20171023214058.128121-12-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Add a new ioctl, FS_IOC_GET_ENCRYPTION_KEY_STATUS. Given a key specified by 'struct fscrypt_key_specifier' (the same way a key is specified for the ioctls which add and remove keys), it returns status information in a 'struct fscrypt_get_key_status_args'. The main motivation for this is that applications need to be able to check whether an encrypted directory is "unlocked" or not, so that they can add the key if it is not, and avoid adding the key (which may involve prompting the user for a passphrase) if it already is. It's possible to use some workarounds such as checking whether opening a regular file fails with ENOKEY, or checking whether the filenames "look like gibberish" or not. However, no workaround is usable in all cases. It's also not a simple matter of locked/unlocked anymore because we also have a partially locked state, where FS_IOC_REMOVE_ENCRYPTION_KEY has removed the secret but some encrypted files are still in use. This difference can be important for applications. Moreover, after later patches some applications will also need a way to determine whether a key was added by the current user vs. by some other user. Ideally we'd have been able to use keyctl_search() to check whether a key is present or not, rather than introducing a new ioctl. However, even if the keyrings permission system was fixed to allow granting read-only access to a keyring (currently the "Search" permission allows keyctl_invalidate()), it still wouldn't work out because the fscrypt master keys can be in states other than just present/absent, as described above. Moreover, we'd still have to at least add an ioctl which retrieves the ID of ->s_master_keys. /proc/keys cannot really be the API either, since reading /proc/keys involves iterating through all keys on the system and is primarily meant as a debugging interface. We also don't necessarily want to grant everyone VIEW access to all the fscrypt keys as that would imply everyone being able to list them as well. Therefore, a new ioctl to get an fscrypt key's status seems like the best solution. It is also consistent with the ioctls to add and remove keys. Signed-off-by: Eric Biggers --- fs/crypto/keyinfo.c | 64 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 6 ++++ include/linux/fscrypt_supp.h | 1 + include/uapi/linux/fscrypt.h | 17 +++++++++++ 4 files changed, 88 insertions(+) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index dc2697cf9114..4052030a4c96 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -566,6 +566,70 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg) } EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key); +/* + * Retrieve the status of an fscrypt master encryption key. + * + * We set ->status to indicate whether the key is absent, present, or + * incompletely removed. "Incompletely removed" means that the master key + * secret has been removed, but some files which had been unlocked with it are + * still in use. This field allows applications to easily determine the state + * of an encrypted directory without using a hack such as trying to open a + * regular file in it (which can confuse the "incompletely removed" state with + * absent or present). + * + * Note: this ioctl only works with keys added to the filesystem-level keyring. + * It does *not* work with keys added via the old mechanism which involved + * process-subscribed keyrings. + */ +int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) +{ + struct super_block *sb = file_inode(filp)->i_sb; + struct fscrypt_get_key_status_args arg; + struct key *key; + struct fscrypt_master_key *mk; + int err; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (memchr_inv(arg.reserved1, 0, sizeof(arg.reserved1))) + return -EINVAL; + + if (!valid_key_spec(&arg.key_spec)) + return -EINVAL; + + arg.reserved2 = 0; + memset(arg.reserved3, 0, sizeof(arg.reserved3)); + + key = find_master_key(sb, &arg.key_spec); + if (IS_ERR(key)) { + if (key != ERR_PTR(-ENOKEY)) + return PTR_ERR(key); + arg.status = FSCRYPT_KEY_STATUS_ABSENT; + err = 0; + goto out; + } + mk = key->payload.data[0]; + down_read(&key->sem); + + if (!is_master_key_secret_present(&mk->mk_secret)) { + arg.status = FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED; + err = 0; + goto out_release_key; + } + + arg.status = FSCRYPT_KEY_STATUS_PRESENT; + err = 0; +out_release_key: + up_read(&key->sem); + key_put(key); +out: + if (!err && copy_to_user(uarg, &arg, sizeof(arg))) + err = -EFAULT; + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_key_status); + static void derive_crypt_complete(struct crypto_async_request *req, int rc) { struct fscrypt_completion_result *ecr = req->data; diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 92616bfdc294..bd60f951b06a 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -95,6 +95,12 @@ static inline int fscrypt_ioctl_remove_key(struct file *filp, return -EOPNOTSUPP; } +static inline int fscrypt_ioctl_get_key_status(struct file *filp, + void __user *arg) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_get_encryption_info(struct inode *inode) { return -EOPNOTSUPP; diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 620ca4f1bafe..ace278056dbe 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -44,6 +44,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, /* keyinfo.c */ extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg); extern int fscrypt_ioctl_remove_key(struct file *filp, const void __user *arg); +extern int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg); extern int fscrypt_get_encryption_info(struct inode *); extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 5d02f138668c..9da153df238a 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -72,11 +72,28 @@ struct fscrypt_remove_key_args { struct fscrypt_key_specifier key_spec; }; +/* Struct passed to FS_IOC_GET_ENCRYPTION_KEY_STATUS */ +struct fscrypt_get_key_status_args { + /* input */ + __u64 reserved1[3]; + struct fscrypt_key_specifier key_spec; + + /* output */ + __u32 status; +#define FSCRYPT_KEY_STATUS_ABSENT 1 +#define FSCRYPT_KEY_STATUS_PRESENT 2 +#define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 + __u32 reserved2; + + __u64 reserved3[7]; +}; + #define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy) #define FS_IOC_GET_ENCRYPTION_PWSALT _IOW( 'f', 20, __u8[16]) #define FS_IOC_GET_ENCRYPTION_POLICY _IOW( 'f', 21, struct fscrypt_policy) #define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 22, struct fscrypt_add_key_args) #define FS_IOC_REMOVE_ENCRYPTION_KEY _IOR( 'f', 23, struct fscrypt_remove_key_args) +#define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f',24, struct fscrypt_get_key_status_args) /**********************************************************************/ From patchwork Mon Oct 23 21:40:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829653 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="MczdoO+H"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHc6DzDz9sRg for ; Tue, 24 Oct 2017 08:42:36 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932201AbdJWVmf (ORCPT ); Mon, 23 Oct 2017 17:42:35 -0400 Received: from mail-it0-f67.google.com ([209.85.214.67]:56831 "EHLO mail-it0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932173AbdJWVm0 (ORCPT ); Mon, 23 Oct 2017 17:42:26 -0400 Received: by mail-it0-f67.google.com with SMTP id r127so7684788itb.5; Mon, 23 Oct 2017 14:42:26 -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=R6C2AaIneOIoXK0xAiQxuXlGvMlgmSYIIzFiV5vgznY=; b=MczdoO+HetE3uCJyjN+DCLsHsvFeX1TzOL3WXkBil7ZkL1m43GHCycyWaUomKXl91t PQ8akUqFyGA7RPRzJd/3+dWC6y8CVNi8qj9UuxNCveB4sN3mxgkKVyHkReUSeAa06+Il w1e0Rkax/1jQxpBHSUYrVgoRuCoowqRk8u7gxDok7+GMwe9YL/sK6dQgY0QI3hy7IqTj OjCLSj3lxblvqbeWasnV4i6Ifh0yKU5/Dk4QRz+qv/N+BzaSTTT5Z3VXwtR4h6+7dsSI TGZTkR0dQ1Z/Dgci9OMZh4cwLhvEiybaYzT3q9fNLuz+s5zK4blALf+9WqJ1sAfgHRny Kk3g== 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=R6C2AaIneOIoXK0xAiQxuXlGvMlgmSYIIzFiV5vgznY=; b=De6udkpArkJ2/dQ+8JQ2whGTmO1+wsHzDF7i9UmR/u5/jUwKaWUFfIw6Jrna8AOOSx X94ADg2x53ZZj4IYqT11qEvmJZVGo4RsmLIHV87R2pvC5Q5C9ZMCRskrFpJEe2ZeeuRI kshAyzab7JEDjHo7hmb9r4XpY74iBxG+PBuLhVR/0XqpAaBSj8OXlNozt0sCZde0+6rR lfBzU560kVKu7ozdqzb1P8jaPabQ+fvBxnbkbHe3A1xHrG3S7pUM0gKnzboP/cGifMxd WamXyg04jMFvtoEuVhGIzKUjfVUg/d9BzoDDmcqdFfsWlbCzeyhu+XWHYw6lXKrYix9Q taKQ== X-Gm-Message-State: AMCzsaWTVgj1jYJgbQlJU3tf0MMmG+zpNIWDv1DM0DP8Y+0i9exd3uQi vKZTLx/4HDRGzSYwZ9nHxUS1aQkL X-Google-Smtp-Source: ABhQp+RSUFcy9d6MFUB0UUAg7tIAbr8MIaz9+cjaBCaDvK3Y2v0xXa6vpnRnD/F4RDZ6BQ7bNUNEGQ== X-Received: by 10.36.233.196 with SMTP id f187mr10615019ith.14.1508794945434; Mon, 23 Oct 2017 14:42:25 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.24 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:25 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 12/25] ext4 crypto: wire up new ioctls for managing encryption keys Date: Mon, 23 Oct 2017 14:40:45 -0700 Message-Id: <20171023214058.128121-13-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Signed-off-by: Eric Biggers --- fs/ext4/ioctl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index afb66d4ab5cf..b8a6765a556f 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -978,6 +978,21 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case EXT4_IOC_GET_ENCRYPTION_POLICY: return fscrypt_ioctl_get_policy(filp, (void __user *)arg); + case FS_IOC_ADD_ENCRYPTION_KEY: + if (!ext4_has_feature_encrypt(sb)) + return -EOPNOTSUPP; + return fscrypt_ioctl_add_key(filp, (void __user *)arg); + + case FS_IOC_REMOVE_ENCRYPTION_KEY: + if (!ext4_has_feature_encrypt(sb)) + return -EOPNOTSUPP; + return fscrypt_ioctl_remove_key(filp, (const void __user *)arg); + + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + if (!ext4_has_feature_encrypt(sb)) + return -EOPNOTSUPP; + return fscrypt_ioctl_get_key_status(filp, (void __user *)arg); + case EXT4_IOC_FSGETXATTR: { struct fsxattr fa; @@ -1102,6 +1117,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC_SET_ENCRYPTION_POLICY: case EXT4_IOC_GET_ENCRYPTION_PWSALT: case EXT4_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_ADD_ENCRYPTION_KEY: + case FS_IOC_REMOVE_ENCRYPTION_KEY: + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: case EXT4_IOC_SHUTDOWN: case FS_IOC_GETFSMAP: break; From patchwork Mon Oct 23 21:40:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829652 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="gWqezrSC"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHc18Frz9sPr for ; Tue, 24 Oct 2017 08:42:36 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932194AbdJWVmb (ORCPT ); Mon, 23 Oct 2017 17:42:31 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:52908 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932176AbdJWVm1 (ORCPT ); Mon, 23 Oct 2017 17:42:27 -0400 Received: by mail-io0-f196.google.com with SMTP id f20so21700658ioj.9; Mon, 23 Oct 2017 14:42:27 -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=jAhGMqQsViLdQ28Cp4m3d4i0dN1WL0MwsTETCvPIBwA=; b=gWqezrSCxDld13xUz5iB2icbznkayxMKXBPaNFNGz3aZAln6Dj6L0WVWesoVJ5gcJ+ ky4ctyzenvlSCM7k0TiyNbdmI++d+l3BPnCcNVtZunlu4UdVbuEuHPOXIHG+FaQzxYx7 MN2qFHwj+uoi2MGDSmqponQpWJs42LVb/DnwT4y/BN1vmY2hUr/XK7JdavfsO/srs4s0 NhHaPMlH7ovEJibVnM3MA2P+ARiLakODremgrjk5vxj7j11IgaSDOE5OB5Q4lD4oLNLe Q6aCjfVyD3uLtfpMxEAK/lcT1W3vZkRVdqPAHBLrovGRNAWV/Q4IBf+sPwLa0T7KoWJW jvuA== 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=jAhGMqQsViLdQ28Cp4m3d4i0dN1WL0MwsTETCvPIBwA=; b=RH6ktc4ZbrNDaZKar9gDO3pZTJJBZ9TJEzZnM0kYGNiRb/VI38SUDvKuWo0GiwnEFw NGUhP4PLc8/IxRS6ydY/bU+Rrzx7ArepJuOHswxS2RXDyoVKLSxrJ7WFW8/fekCvT/HC mm6uHDCJMfZCBYYYUBdTLfJGu1+KgpQ6vqV9C6yveGCT/zutC29GepWng2QOuBWxd7LJ l/B2rP3wus0qB8pCM8DJ6g+42SswoSLOYbrgW3y+Qr1txJxrO7mey2NGtQMvfBTxurCx WrbcZ8uUdPaQl98C43XXmi7Jg2q6ApxWTf266LOjfb+e69e9BQsP1ZmXixs0c/Ai5Kdp yvOg== X-Gm-Message-State: AMCzsaXN8/J5t/3Mej+grmgndbjcB2jIu1MO6mEyqejUQOvPCXvdGtvO VF32enBt8vY7IAQ067Rwt9UlhkLC X-Google-Smtp-Source: ABhQp+TC6a5lFG+uMjQBPXOwSNra3BElECyumBqazBHOEhaudVt6HRr/Jo5GenPtAyKladPibxwy3A== X-Received: by 10.107.186.87 with SMTP id k84mr19309249iof.121.1508794946719; Mon, 23 Oct 2017 14:42:26 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:26 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 13/25] f2fs crypto: wire up new ioctls for managing encryption keys Date: Mon, 23 Oct 2017 14:40:46 -0700 Message-Id: <20171023214058.128121-14-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Signed-off-by: Eric Biggers --- fs/f2fs/file.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 517e112c8a9a..0296a9594fe7 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2651,6 +2651,12 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_get_encryption_policy(filp, arg); case F2FS_IOC_GET_ENCRYPTION_PWSALT: return f2fs_ioc_get_encryption_pwsalt(filp, arg); + case FS_IOC_ADD_ENCRYPTION_KEY: + return fscrypt_ioctl_add_key(filp, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(filp, (const void __user *)arg); + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(filp, (void __user *)arg); case F2FS_IOC_GARBAGE_COLLECT: return f2fs_ioc_gc(filp, arg); case F2FS_IOC_GARBAGE_COLLECT_RANGE: @@ -2731,6 +2737,9 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_SET_ENCRYPTION_POLICY: case F2FS_IOC_GET_ENCRYPTION_PWSALT: case F2FS_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_ADD_ENCRYPTION_KEY: + case FS_IOC_REMOVE_ENCRYPTION_KEY: + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: case F2FS_IOC_GARBAGE_COLLECT: case F2FS_IOC_GARBAGE_COLLECT_RANGE: case F2FS_IOC_WRITE_CHECKPOINT: From patchwork Mon Oct 23 21:40:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829670 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="VGPzTcZo"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKC3Dbpz9sNc for ; Tue, 24 Oct 2017 08:43:59 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932344AbdJWVnz (ORCPT ); Mon, 23 Oct 2017 17:43:55 -0400 Received: from mail-io0-f195.google.com ([209.85.223.195]:50614 "EHLO mail-io0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932182AbdJWVm3 (ORCPT ); Mon, 23 Oct 2017 17:42:29 -0400 Received: by mail-io0-f195.google.com with SMTP id 97so21701012iok.7; Mon, 23 Oct 2017 14:42:28 -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=QUfHkpecBlLdLVUNqn67/PgAG+kEvbbZIrssE94eL6s=; b=VGPzTcZoDtmaIFyxFbGwrU4iDmMt8zk9KIp/BkYfI2I1WlPPXuortAGSY7LHBncdDv MXUnYnZNttnx3vDNNeO4GadXZrCPnlic/+t3FticaJ6bZHnjYAADoRVGExyhNzsaDc89 iIMxM9jb7zBgdVo4jK/F/lo5N1ds1J9tey1QC+s64HkjFmdCjC1EH2fTZISR01ssHaiT WgzkEKoEj1LSFHbepybfb3EEqD5MaNr0pG/PWtziFox1mqqwo8Kw2RG/zOuv2rdmto8u 5qCi4lXRJWLWrOZOryurd/zFg2EBZwY0tSqsGSSJSggpUH3KvIdElAww8+GnSWD+W/D2 JBlQ== 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=QUfHkpecBlLdLVUNqn67/PgAG+kEvbbZIrssE94eL6s=; b=gco0EfjsCo87BnXHk0CZvWxJxRulCQ8Z6m/WJoTdiI6PUMZNACPDDX7gH2TS+p3EHB VN1WgppePLIDYmTB7SD14O3Y8FCvQaMhcyddKmPxz32Jm7H6zVUVmcClsQJkAziF0oj2 +eXBoqohWX3PYHJex9R98SY+K8SIFLbf28WWyXDrv9MRPrQ5Cf1gdSke+vlFz7UOcJp3 AI3pAhPU/TDRcTwciCc0veJkwqE+R3oEysY15m79N97fXKr8jwSG1A30UMGiWfEV+HA0 Fas/wo7ZGDKVZnmpzti7jimOs5c1+bi4CZcO5BMaO78IGmhEZKADzPqjnlNuvN62/nx/ mlTw== X-Gm-Message-State: AMCzsaWjyzVSior1OMW+RV6gKudmTOhwc0dAUd4VI6xxEW/Wf1If4htC lK/DLlu40EMlZWcJS3tph7xQsmOq X-Google-Smtp-Source: ABhQp+TPuEafxtwHdHrn3WFN7nQ7gLOS4bDqSLYIa6OozxpNhD68RV709wXkOz9M6Nd1cbtE9tr8jQ== X-Received: by 10.107.192.71 with SMTP id q68mr6542203iof.81.1508794947960; Mon, 23 Oct 2017 14:42:27 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.26 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:27 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 14/25] ubifs crypto: wire up new ioctls for managing encryption keys Date: Mon, 23 Oct 2017 14:40:47 -0700 Message-Id: <20171023214058.128121-15-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Signed-off-by: Eric Biggers --- fs/ubifs/ioctl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 0164bcc827f8..09ede2d1898f 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -205,6 +205,15 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #endif } + case FS_IOC_ADD_ENCRYPTION_KEY: + return fscrypt_ioctl_add_key(file, (void __user *)arg); + + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(file, (const void __user *)arg); + + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(file, (void __user *)arg); + default: return -ENOTTY; } @@ -222,6 +231,9 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case FS_IOC_SET_ENCRYPTION_POLICY: case FS_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_ADD_ENCRYPTION_KEY: + case FS_IOC_REMOVE_ENCRYPTION_KEY: + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: break; default: return -ENOIOCTLCMD; From patchwork Mon Oct 23 21:40:48 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829672 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="arrW1K/l"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVKK3v5Sz9sPt for ; Tue, 24 Oct 2017 08:44:05 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932316AbdJWVnx (ORCPT ); Mon, 23 Oct 2017 17:43:53 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:48684 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932184AbdJWVma (ORCPT ); Mon, 23 Oct 2017 17:42:30 -0400 Received: by mail-io0-f193.google.com with SMTP id j17so21704486iod.5; Mon, 23 Oct 2017 14:42:29 -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=OMXcS9y24ubcseOc+pBdDRqhoky0kt8ih38nCA8A/Gs=; b=arrW1K/ldyXztDfm70pKJJSgrCA71CdpqQaaWlmoCHb5qCqNmKGz/YYmoa/YWuP8qh laxUBenf2OnBG0+pOVVzGSP+Mr8QMu/OARUO10Jp+dZJLDIfJgop63Adbud7Es5tPg1s y87hPn35jsQG7wA4paKhqIPiVHOB7txpYZMlRHKmbsW9E2WAvmlq/wpvI3UdUVaJuqBe 0w8WGCH2Qc+QDyzRX8f6plS/vMC3C2dCsbL7VdB5RRR8srN+kPQnArZrEfgoMCKqfvsv 2sEqRqk3EJBfkqfI4Xj0loNbmoZ4xMowiX8Q3YXMaSyZ5etkxeoFD4B5xKpgZGGcwxX4 /7gg== 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=OMXcS9y24ubcseOc+pBdDRqhoky0kt8ih38nCA8A/Gs=; b=ONmWhqcH/YQeilcfsDwtxDplC3qYdUDgZSvlsRhVWupVPTeWlyEvaw789ohOOj5Zqi xPH6le+XH3prg8UtMKtrWFjoOxuacVKbW+s6G/gei7D9G04zr71U9Wz5p/XCy7guwQHd 3h0FXnGWCnG79Zi8YUoUsp46X4XvOnNHqjy0AvJm5NUa1sFIT11aPHgfWzWq+X7VOC59 oX5lIjcfYaCQtprG+GCRhypC8TfP8/1cZds3gcWt0QAYrzyALGRLjgUW/+lHo/U42EEh A/4ytytL/Y0waeWDYXQEvjkdI7JfL6b1tU3FMt5LPK2Is3eDpG4t7rWq25amP3JCwJLE XQ/w== X-Gm-Message-State: AMCzsaWY1zhNM+P/wil0VViefNBwAnrjeNOzAUU1tN0U5EK59ZG8KkO8 5KF882eqdQW5zoReBEEZYK8OYZTS X-Google-Smtp-Source: ABhQp+R/Ok11T+Ua/PLXztktVTW9ixZgtGZE67fAfGplStsc7eZQXQ2dBQck6whUs7AvUc6IamYpNQ== X-Received: by 10.107.169.34 with SMTP id s34mr10247939ioe.103.1508794949256; Mon, 23 Oct 2017 14:42:29 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:28 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 15/25] fscrypt: add UAPI definitions to get/set v2 encryption policies Date: Mon, 23 Oct 2017 14:40:48 -0700 Message-Id: <20171023214058.128121-16-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Currently, fscrypt policies and xattrs identify the master key by master_key_descriptor, which is an arbitrary 8-byte value used to form the description of the keyring key. However, there is no verification that the key descriptor in any way corresponds with the key payload. Since ->i_crypt_info by necessity gets set up on a "first come, first serve" basis, this flaw allows a process with only read-only access to an encrypted file or directory to provide the wrong key, causing the file contents or directory listing to be corrupted. This is a bug with security implications which must be fixed. To fix this bug without simply locking down adding keys to root, we must replace master_key_descriptor with a cryptographic hash of the key. We name the replacement master_key_identifier and make it 16 bytes long, which should provide enough collision resistance, and more importantly preimage resistance, without bloating the size of the encryption xattr too much. This will be both an on-disk format and API change, since we'll need to define new versions of both the fscrypt_context and fscrypt_policy. This patch begins this process by defining the UAPI changes to manage v2 policies. (Note: we jump to version 2 even though the previous policy version number was 0 because the fscrypt_context was actually already using version 1, not version 0. It would be really confusing to have them always be 1 off from each other.) The existing FS_IOC_SET_ENCRYPTION_POLICY will be used to set a v2 policy, as the kernel will be able to examine the 'version' field. However, a new ioctl FS_IOC_GET_ENCRYPTION_POLICY_EX is needed to get a v2 policy, since the returned struct needs to be larger. This ioctl includes a size field as input, so that it can be used for both v1 and v2 policies as well as any new policy versions that may get added in the future. Signed-off-by: Eric Biggers --- include/uapi/linux/fscrypt.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 9da153df238a..065060b20a34 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -7,7 +7,6 @@ * File system encryption support */ /* Policy provided via an ioctl on the topmost directory */ -#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 /* Encryption policy flags */ #define FSCRYPT_POLICY_FLAGS_PAD_4 0x00 @@ -26,13 +25,22 @@ #define FSCRYPT_MODE_AES_128_CBC 5 #define FSCRYPT_MODE_AES_128_CTS 6 -struct fscrypt_policy { +/* + * Legacy policy version; no key verification (potentially insecure). + * For new encrypted directories, use fscrypt_policy_v2 instead. + * + * Careful: the .version field for this is actually 0, not 1. + */ +#define FSCRYPT_POLICY_VERSION_LEGACY 0 +#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 +struct fscrypt_policy_v1 { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; +#define fscrypt_policy fscrypt_policy_v1 /* * Process-subscribed "logon" key description prefix and payload format. @@ -47,6 +55,30 @@ struct fscrypt_key { __u32 size; }; +/* + * New policy version with HKDF and key verification (recommended). + */ +#define FSCRYPT_POLICY_VERSION_2 2 +#define FSCRYPT_KEY_IDENTIFIER_SIZE 16 +struct fscrypt_policy_v2 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 reserved[4]; + __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; +}; + +/* Struct passed to FS_IOC_GET_ENCRYPTION_POLICY_EX */ +struct fscrypt_get_policy_ex_args { + __u64 size; /* input/output */ + union { + __u8 version; + struct fscrypt_policy_v1 v1; + struct fscrypt_policy_v2 v2; + } policy; /* output */ +}; + struct fscrypt_key_specifier { __u32 type; #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 @@ -91,6 +123,7 @@ struct fscrypt_get_key_status_args { #define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy) #define FS_IOC_GET_ENCRYPTION_PWSALT _IOW( 'f', 20, __u8[16]) #define FS_IOC_GET_ENCRYPTION_POLICY _IOW( 'f', 21, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 21, __u8[9]) /* size + version */ #define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 22, struct fscrypt_add_key_args) #define FS_IOC_REMOVE_ENCRYPTION_KEY _IOR( 'f', 23, struct fscrypt_remove_key_args) #define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f',24, struct fscrypt_get_key_status_args) From patchwork Mon Oct 23 21:40:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829668 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="XxlgIprW"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJr74SPz9sNc for ; Tue, 24 Oct 2017 08:43:40 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932334AbdJWVni (ORCPT ); Mon, 23 Oct 2017 17:43:38 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:48685 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932195AbdJWVmc (ORCPT ); Mon, 23 Oct 2017 17:42:32 -0400 Received: by mail-io0-f196.google.com with SMTP id j17so21704554iod.5; Mon, 23 Oct 2017 14:42:31 -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=7PAFel6xMMTAASphSbnZbb/0C93sFMs1mLjjC+b6+Kk=; b=XxlgIprW1p8wqJ7EnJvrIbzQAMlxtZIhq0W2sEA9aFpusCdFFBodwh76BRuehiE05S IMP3+nmdcsdyVt+d8K5K+G+kSbZI+xN7hOHJUC5hlbDLveB4nMUERIDn0uzokmPcUFwL aqe4f0miXgtVG54owVALV8Ej57qjMdFSiD70hfDcvR45Gbloh69GISSOtlOAK5h4A/l0 h8QiMsnj6QBWNjynNAcpA9BEor1Di5hPgbn673KeG9JnaCRZAlc+8JH5obQXwvfGt3mS FJFBKxSBw4VnTqNWeFoPmh5flgFpwnOHAMH+onITwqBoTgFOZ/2/arDa2nKu28qhkU/g Vl/w== 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=7PAFel6xMMTAASphSbnZbb/0C93sFMs1mLjjC+b6+Kk=; b=FHF8Io/HqiPCJFvmYoQoZrt6hrmlZAmHnwaFcIpTJIsL3gF8fY4U4n047JxbiyV12t CcEopG6btkXkMBA1iqniWOTOOXjDv3NM012vq8BiSzTFEOzt+mH3gHPbx0uKZUxEv5Tg UG7w54I+K443ZV9Zh2s3zJhc9BrgrpKsft/sbgwJlBOOnWeP+sNBlzN08ndcMyZm1obK aQzl2lvuZ/yhtePg/VqCIndp09DZPlUntG/OC2g4pHY170Az9+I99swZTdVxf9r/Z+Da +zs1ugOvkB6Tncq6pu03Zs9DfqVT4056e6zddp/rn/jTLK69zYnX84KmBtaGYvn1aTIK 18gQ== X-Gm-Message-State: AMCzsaWhUw3uosMp2vQS73v7xWmV6y12j37vfPKyPvD1KUfzS/oyiWRA Fw21+Z1M0RF9a0VTC9s6I2n4fDJ8 X-Google-Smtp-Source: ABhQp+TGJOqheSJHlCLfFqwXk0Fgb7N42Pp5FFoKpjhXWIx34Sz2TefB3kOC6wC3V3YzPXf8c0u58A== X-Received: by 10.107.31.143 with SMTP id f137mr10182620iof.86.1508794950809; Mon, 23 Oct 2017 14:42:30 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:30 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 16/25] fscrypt: implement basic handling of v2 encryption policies Date: Mon, 23 Oct 2017 14:40:49 -0700 Message-Id: <20171023214058.128121-17-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Update the fscrypt internals to handle v2 encryption policies. This includes supporting getting and setting them, translating them to/from the on-disk fscrypt_context. It also includes storing either a v1 or v2 policy struct in the fscrypt_info for use by fscrypt_inherit_context() and fscrypt_has_permitted_context(). (Previously we were storing the individual fields, but it is a bit easier to store a policy struct.) An fscrypt_policy_v1 (previously 'fscrypt_policy') maps to/from an fscrypt_context_v1 (previously 'fscrypt_context'), while an fscrypt_policy_v2 maps to/from an fscrypt_context_v2. Key management for v2 policies will be implemented by later patches. For now, attempting to set up an inode's key just fails with EOPNOTSUPP. Signed-off-by: Eric Biggers --- fs/crypto/fname.c | 4 +- fs/crypto/fscrypt_private.h | 172 ++++++++++++++++--- fs/crypto/keyinfo.c | 70 ++++---- fs/crypto/policy.c | 368 ++++++++++++++++++++++++++++------------ include/linux/fscrypt.h | 2 +- include/linux/fscrypt_notsupp.h | 6 + include/linux/fscrypt_supp.h | 1 + 7 files changed, 452 insertions(+), 171 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index c91bcef65b9f..78dc0e3f6328 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -46,7 +46,7 @@ static int fname_encrypt(struct inode *inode, int res = 0; char iv[FS_CRYPTO_BLOCK_SIZE]; struct scatterlist sg; - int padding = 4 << (ci->ci_flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); + int padding = fscrypt_policy_fname_padding(&ci->ci_policy); unsigned int lim; unsigned int cryptlen; @@ -217,7 +217,7 @@ u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen) struct fscrypt_info *ci = inode->i_crypt_info; if (ci) - padding = 4 << (ci->ci_flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); + padding = fscrypt_policy_fname_padding(&ci->ci_policy); ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE); return round_up(ilen, padding); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 2fdc4e5c0771..dec85c4b14a8 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -29,39 +29,159 @@ #define FSCRYPT_MIN_KEY_SIZE 16 -/** - * Encryption context for inode - * - * Protector format: - * 1 byte: Protector format (1 = this version) - * 1 byte: File contents encryption mode - * 1 byte: File names encryption mode - * 1 byte: Flags - * 8 bytes: Master Key descriptor - * 16 bytes: Encryption Key derivation nonce - */ -struct fscrypt_context { - u8 format; +struct fscrypt_context_v1 { + + u8 version; /* FSCRYPT_CONTEXT_V1 */ + + /* Same meaning as in v2 context --- see below */ u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; + + /* + * Descriptor for this file's master key in a process-subscribed keyring + * --- typically a session keyring, or a user keyring linked into a + * session or user session keyring. This is an arbitrary value, chosen + * by userspace when it set the encryption policy. It is *not* + * necessarily tied to the actual key payload. + */ u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + + /* + * A unique value used in combination with the master key to derive the + * file's actual encryption key + */ u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; -} __packed; +}; + +struct fscrypt_context_v2 { + + u8 version; /* FSCRYPT_CONTEXT_V2 */ + + /* Encryption mode for the contents of regular files */ + u8 contents_encryption_mode; -#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 + /* Encryption mode for filenames in directories and symlink targets */ + u8 filenames_encryption_mode; + + /* Options that affect how encryption is done (e.g. padding amount) */ + u8 flags; + + /* Reserved, must be 0 */ + u8 reserved[4]; + + /* + * A cryptographic hash of the master key with which this file is + * encrypted + */ + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + + /* + * A unique value used in combination with the master key to derive the + * file's actual encryption key + */ + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +}; + +/** + * fscrypt_context - the encryption context for an inode + * + * Filesystems usually store this in an extended attribute. It identifies the + * encryption algorithm and key with which the file is encrypted. + * + * Since this is stored on-disk, be careful not to reorder fields or add any + * implicit padding bytes! + */ +union fscrypt_context { + struct fscrypt_context_v1 v1; + struct fscrypt_context_v2 v2; +}; + +#define FSCRYPT_CONTEXT_V1 1 +#define FSCRYPT_CONTEXT_V2 2 + +static inline int fscrypt_context_size(const union fscrypt_context *ctx) +{ + switch (ctx->v1.version) { + case FSCRYPT_CONTEXT_V1: + return sizeof(ctx->v1); + case FSCRYPT_CONTEXT_V2: + return sizeof(ctx->v2); + } + return 0; +} + +static inline bool +fscrypt_valid_context_format(const union fscrypt_context *ctx, int size) +{ + return size >= 1 && size == fscrypt_context_size(ctx); +} + +#undef fscrypt_policy +union fscrypt_policy { + struct fscrypt_policy_v1 v1; + struct fscrypt_policy_v2 v2; +}; + +static inline int fscrypt_policy_size(const union fscrypt_policy *policy) +{ + switch (policy->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: + return sizeof(policy->v1); + case FSCRYPT_POLICY_VERSION_2: + return sizeof(policy->v2); + } + return 0; +} + +static inline u8 +fscrypt_policy_contents_mode(const union fscrypt_policy *policy) +{ + switch (policy->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: + return policy->v1.contents_encryption_mode; + case FSCRYPT_POLICY_VERSION_2: + return policy->v2.contents_encryption_mode; + } + BUG(); +} + +static inline u8 +fscrypt_policy_fnames_mode(const union fscrypt_policy *policy) +{ + switch (policy->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: + return policy->v1.filenames_encryption_mode; + case FSCRYPT_POLICY_VERSION_2: + return policy->v2.filenames_encryption_mode; + } + BUG(); +} + +static inline int +fscrypt_policy_fname_padding(const union fscrypt_policy *policy) +{ + switch (policy->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: + return 4 << (policy->v1.flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); + case FSCRYPT_POLICY_VERSION_2: + return 4 << (policy->v2.flags & FSCRYPT_POLICY_FLAGS_PAD_MASK); + } + BUG(); +} /* - * A pointer to this structure is stored in the file system's in-core - * representation of an inode. + * fscrypt_info - the "encryption key" for an inode + * + * When an encrypted file's key is made available, an instance of this struct is + * allocated and stored in ->i_crypt_info. Once created, it remains until the + * inode is evicted. */ struct fscrypt_info { - u8 ci_data_mode; - u8 ci_filename_mode; - u8 ci_flags; + + /* The actual crypto transforms needed for encryption and decryption */ struct crypto_skcipher *ci_ctfm; struct crypto_cipher *ci_essiv_tfm; - u8 ci_master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; /* * The master key with which this inode was unlocked (decrypted). This @@ -78,6 +198,9 @@ struct fscrypt_info { * ->ci_master_key is set. */ struct inode *ci_inode; + + /* The encryption policy used by this file */ + union fscrypt_policy ci_policy; }; typedef enum { @@ -114,4 +237,11 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, extern struct key_type key_type_fscrypt_mk; extern void __exit fscrypt_essiv_cleanup(void); +/* policy.c */ +extern bool fscrypt_policies_equal(const union fscrypt_policy *policy1, + const union fscrypt_policy *policy2); +extern bool fscrypt_supported_policy(const union fscrypt_policy *policy_u); +extern void fscrypt_context_to_policy(const union fscrypt_context *ctx_u, + union fscrypt_policy *policy_u); + #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 4052030a4c96..937a678ebba1 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -649,7 +649,7 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) * key is longer, then only the first 'derived_keysize' bytes are used. */ static int derive_key_aes(const u8 *master_key, - const struct fscrypt_context *ctx, + const struct fscrypt_context_v1 *ctx, u8 *derived_key, unsigned int derived_keysize) { int err; @@ -751,7 +751,7 @@ find_and_lock_process_key(const char *prefix, } static int find_and_derive_key_legacy(const struct inode *inode, - const struct fscrypt_context *ctx, + const struct fscrypt_context_v1 *ctx, u8 *derived_key, unsigned int derived_keysize) { @@ -786,7 +786,7 @@ static int find_and_derive_key_legacy(const struct inode *inode, * its fscrypt_info into ->mk_decrypted_inodes. */ static int find_and_derive_key(const struct inode *inode, - const struct fscrypt_context *ctx, + const union fscrypt_context *ctx, u8 *derived_key, unsigned int derived_keysize, struct key **master_key_ret) { @@ -795,9 +795,15 @@ static int find_and_derive_key(const struct inode *inode, struct fscrypt_key_specifier mk_spec; int err; - mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; - memcpy(mk_spec.descriptor, ctx->master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE); + switch (ctx->v1.version) { + case FSCRYPT_CONTEXT_V1: + mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; + memcpy(mk_spec.descriptor, ctx->v1.master_key_descriptor, + FSCRYPT_KEY_DESCRIPTOR_SIZE); + break; + default: + return -EOPNOTSUPP; + } key = find_master_key(inode->i_sb, &mk_spec); if (IS_ERR(key)) { @@ -807,7 +813,7 @@ static int find_and_derive_key(const struct inode *inode, * As a legacy fallback, we search the current task's subscribed * keyrings in addition to ->s_master_keys. */ - return find_and_derive_key_legacy(inode, ctx, derived_key, + return find_and_derive_key_legacy(inode, &ctx->v1, derived_key, derived_keysize); } mk = key->payload.data[0]; @@ -833,7 +839,7 @@ static int find_and_derive_key(const struct inode *inode, goto out_release_key; } - err = derive_key_aes(mk->mk_secret.raw, ctx, + err = derive_key_aes(mk->mk_secret.raw, &ctx->v1, derived_key, derived_keysize); if (err) goto out_release_key; @@ -862,17 +868,10 @@ static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode, { u32 mode; - if (!fscrypt_valid_enc_modes(ci->ci_data_mode, ci->ci_filename_mode)) { - pr_warn_ratelimited("fscrypt: inode %lu uses unsupported encryption modes (contents mode %d, filenames mode %d)\n", - inode->i_ino, - ci->ci_data_mode, ci->ci_filename_mode); - return -EINVAL; - } - if (S_ISREG(inode->i_mode)) { - mode = ci->ci_data_mode; + mode = fscrypt_policy_contents_mode(&ci->ci_policy); } else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { - mode = ci->ci_filename_mode; + mode = fscrypt_policy_fnames_mode(&ci->ci_policy); } else { WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n", inode->i_ino, (inode->i_mode & S_IFMT)); @@ -984,7 +983,7 @@ void __exit fscrypt_essiv_cleanup(void) int fscrypt_get_encryption_info(struct inode *inode) { struct fscrypt_info *crypt_info; - struct fscrypt_context ctx; + union fscrypt_context ctx; struct crypto_skcipher *ctfm; const char *cipher_str; unsigned int derived_keysize; @@ -1006,33 +1005,31 @@ int fscrypt_get_encryption_info(struct inode *inode) return res; /* Fake up a context for an unencrypted directory */ memset(&ctx, 0, sizeof(ctx)); - ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - ctx.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; - ctx.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; - memset(ctx.master_key_descriptor, 0x42, + ctx.v1.version = FSCRYPT_CONTEXT_V1; + ctx.v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; + ctx.v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; + memset(ctx.v1.master_key_descriptor, 0x42, FSCRYPT_KEY_DESCRIPTOR_SIZE); - } else if (res != sizeof(ctx)) { - return -EINVAL; + res = sizeof(ctx.v1); } - if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) - return -EINVAL; - - if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID) + if (!fscrypt_valid_context_format(&ctx, res)) return -EINVAL; crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS); if (!crypt_info) return -ENOMEM; - crypt_info->ci_flags = ctx.flags; - crypt_info->ci_data_mode = ctx.contents_encryption_mode; - crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; - memcpy(crypt_info->ci_master_key_descriptor, ctx.master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE); + fscrypt_context_to_policy(&ctx, &crypt_info->ci_policy); + if (!fscrypt_supported_policy(&crypt_info->ci_policy)) { + res = -EINVAL; + pr_warn_ratelimited("fscrypt: inode %lu uses unsupported encryption policy\n", + inode->i_ino); + goto out; + } - res = determine_cipher_type(crypt_info, inode, - &cipher_str, &derived_keysize); + res = determine_cipher_type(crypt_info, inode, &cipher_str, + &derived_keysize); if (res) goto out; @@ -1065,7 +1062,8 @@ int fscrypt_get_encryption_info(struct inode *inode) goto out; if (S_ISREG(inode->i_mode) && - crypt_info->ci_data_mode == FSCRYPT_MODE_AES_128_CBC) { + (fscrypt_policy_contents_mode(&crypt_info->ci_policy) == + FSCRYPT_MODE_AES_128_CBC)) { res = init_essiv_generator(crypt_info, derived_key, derived_keysize); if (res) { diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index a856c8941be6..27a391038f73 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -6,6 +6,7 @@ * * Written by Michael Halcrow, 2015. * Modified by Jaegeuk Kim, 2015. + * Modified by Eric Biggers, 2017 for v2 policy support. */ #include @@ -13,84 +14,227 @@ #include #include "fscrypt_private.h" -/* - * check whether an encryption policy is consistent with an encryption context - */ -static bool is_encryption_context_consistent_with_policy( - const struct fscrypt_context *ctx, - const struct fscrypt_policy *policy) +bool fscrypt_policies_equal(const union fscrypt_policy *policy1, + const union fscrypt_policy *policy2) +{ + if (policy1->v1.version != policy2->v1.version) + return false; + + return !memcmp(policy1, policy2, fscrypt_policy_size(policy1)); +} + +bool fscrypt_supported_policy(const union fscrypt_policy *policy_u) +{ + switch (policy_u->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: { + const struct fscrypt_policy_v1 *policy = &policy_u->v1; + + if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) + return false; + + if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) + return false; + + return true; + } + case FSCRYPT_POLICY_VERSION_2: { + const struct fscrypt_policy_v2 *policy = &policy_u->v2; + + if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) + return false; + + if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) + return false; + + if (memchr_inv(policy->reserved, 0, sizeof(policy->reserved))) + return false; + + return true; + } + } + return false; +} + +static void fscrypt_policy_to_context(const union fscrypt_policy *policy_u, + union fscrypt_context *ctx_u) { - return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && - (ctx->flags == policy->flags) && - (ctx->contents_encryption_mode == - policy->contents_encryption_mode) && - (ctx->filenames_encryption_mode == - policy->filenames_encryption_mode); + memset(ctx_u, 0, sizeof(*ctx_u)); + + switch (policy_u->v1.version) { + case FSCRYPT_POLICY_VERSION_LEGACY: { + const struct fscrypt_policy_v1 *policy = &policy_u->v1; + struct fscrypt_context_v1 *ctx = &ctx_u->v1; + + ctx->version = FSCRYPT_CONTEXT_V1; + ctx->contents_encryption_mode = + policy->contents_encryption_mode; + ctx->filenames_encryption_mode = + policy->filenames_encryption_mode; + ctx->flags = policy->flags; + memcpy(ctx->master_key_descriptor, + policy->master_key_descriptor, + sizeof(ctx->master_key_descriptor)); + get_random_bytes(ctx->nonce, sizeof(ctx->nonce)); + break; + } + case FSCRYPT_POLICY_VERSION_2: { + const struct fscrypt_policy_v2 *policy = &policy_u->v2; + struct fscrypt_context_v2 *ctx = &ctx_u->v2; + + ctx->version = FSCRYPT_CONTEXT_V2; + ctx->contents_encryption_mode = + policy->contents_encryption_mode; + ctx->filenames_encryption_mode = + policy->filenames_encryption_mode; + ctx->flags = policy->flags; + memcpy(ctx->reserved, policy->reserved, sizeof(ctx->reserved)); + memcpy(ctx->master_key_identifier, + policy->master_key_identifier, + sizeof(ctx->master_key_identifier)); + get_random_bytes(ctx->nonce, sizeof(ctx->nonce)); + break; + } + default: + BUG(); + } } -static int create_encryption_context_from_policy(struct inode *inode, - const struct fscrypt_policy *policy) +void fscrypt_context_to_policy(const union fscrypt_context *ctx_u, + union fscrypt_policy *policy_u) { - struct fscrypt_context ctx; + memset(policy_u, 0, sizeof(*policy_u)); + + switch (ctx_u->v1.version) { + case FSCRYPT_CONTEXT_V1: { + const struct fscrypt_context_v1 *ctx = &ctx_u->v1; + struct fscrypt_policy_v1 *policy = &policy_u->v1; + + policy->version = FSCRYPT_POLICY_VERSION_LEGACY; + policy->contents_encryption_mode = + ctx->contents_encryption_mode; + policy->filenames_encryption_mode = + ctx->filenames_encryption_mode; + policy->flags = ctx->flags; + memcpy(policy->master_key_descriptor, + ctx->master_key_descriptor, + sizeof(policy->master_key_descriptor)); + return; + } + case FSCRYPT_CONTEXT_V2: { + const struct fscrypt_context_v2 *ctx = &ctx_u->v2; + struct fscrypt_policy_v2 *policy = &policy_u->v2; + + policy->version = FSCRYPT_POLICY_VERSION_2; + policy->contents_encryption_mode = + ctx->contents_encryption_mode; + policy->filenames_encryption_mode = + ctx->filenames_encryption_mode; + policy->flags = ctx->flags; + memcpy(policy->reserved, ctx->reserved, + sizeof(policy->reserved)); + memcpy(policy->master_key_identifier, + ctx->master_key_identifier, + sizeof(policy->master_key_identifier)); + return; + } + default: + BUG(); + } +} - ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE); +static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy) +{ + union fscrypt_context ctx; + int ret; + + if (inode->i_crypt_info) { + *policy = inode->i_crypt_info->ci_policy; + return 0; + } + + if (!IS_ENCRYPTED(inode)) + return -ENODATA; - if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, - policy->filenames_encryption_mode)) + ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (ret < 0) + return (ret == -ERANGE) ? -EINVAL : ret; + if (!fscrypt_valid_context_format(&ctx, ret)) return -EINVAL; + fscrypt_context_to_policy(&ctx, policy); + return 0; +} + +static int set_encryption_policy(struct inode *inode, + const union fscrypt_policy *policy) +{ + union fscrypt_context ctx; - if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) + if (!fscrypt_supported_policy(policy)) return -EINVAL; - ctx.contents_encryption_mode = policy->contents_encryption_mode; - ctx.filenames_encryption_mode = policy->filenames_encryption_mode; - ctx.flags = policy->flags; - BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE); - get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); + fscrypt_policy_to_context(policy, &ctx); + + if (policy->v1.version == FSCRYPT_POLICY_VERSION_LEGACY) { + /* + * The original encryption policy version provided no way of + * verifying that the correct master key was supplied, which was + * insecure in scenarios where multiple users have access to the + * same encrypted files (even just read-only access). The new + * encryption policy version fixes this and also implies use of + * an improved key derivation function and allows non-root users + * to securely remove keys. So as long as compatibility with + * old kernels isn't required, it is recommended to use the new + * policy version for all new encrypted directories. + */ + pr_warn_once("%s (pid %d) is setting less secure v1 encryption policy; recommend upgrading to v2.\n", + current->comm, current->pid); + } - return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL); + return inode->i_sb->s_cop->set_context(inode, &ctx, + fscrypt_context_size(&ctx), + NULL); } int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) { - struct fscrypt_policy policy; + union fscrypt_policy policy; + union fscrypt_policy existing_policy; struct inode *inode = file_inode(filp); + int size; int ret; - struct fscrypt_context ctx; - if (copy_from_user(&policy, arg, sizeof(policy))) + if (copy_from_user(&policy, arg, sizeof(u8))) + return -EFAULT; + + size = fscrypt_policy_size(&policy); + if (size == 0) + return -EINVAL; + + if (copy_from_user((u8 *)&policy + 1, arg + 1, size - 1)) return -EFAULT; if (!inode_owner_or_capable(inode)) return -EACCES; - if (policy.version != 0) - return -EINVAL; - ret = mnt_want_write_file(filp); if (ret) return ret; inode_lock(inode); - ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + ret = fscrypt_get_policy(inode, &existing_policy); if (ret == -ENODATA) { if (!S_ISDIR(inode->i_mode)) ret = -ENOTDIR; else if (!inode->i_sb->s_cop->empty_dir(inode)) ret = -ENOTEMPTY; else - ret = create_encryption_context_from_policy(inode, - &policy); - } else if (ret == sizeof(ctx) && - is_encryption_context_consistent_with_policy(&ctx, - &policy)) { - /* The file already uses the same encryption policy. */ - ret = 0; - } else if (ret >= 0 || ret == -ERANGE) { + ret = set_encryption_policy(inode, &policy); + } else if (ret == -EINVAL || + (ret == 0 && !fscrypt_policies_equal(&policy, + &existing_policy))) { /* The file already uses a different encryption policy. */ ret = -EEXIST; } @@ -102,36 +246,61 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) } EXPORT_SYMBOL(fscrypt_ioctl_set_policy); +/* Original ioctl version; can only get the original policy version */ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) { - struct inode *inode = file_inode(filp); - struct fscrypt_context ctx; - struct fscrypt_policy policy; - int res; + union fscrypt_policy policy; + int err; - if (!IS_ENCRYPTED(inode)) - return -ENODATA; + err = fscrypt_get_policy(file_inode(filp), &policy); + if (err) + return err; - res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); - if (res < 0 && res != -ERANGE) - return res; - if (res != sizeof(ctx)) + if (policy.v1.version != FSCRYPT_POLICY_VERSION_LEGACY) return -EINVAL; - if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) + + if (copy_to_user(arg, &policy, sizeof(policy.v1))) + return -EFAULT; + return 0; +} +EXPORT_SYMBOL(fscrypt_ioctl_get_policy); + +/* Extended ioctl version; can get policies of any version */ +int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *_arg) +{ + struct fscrypt_get_policy_ex_args __user *arg = _arg; + __u64 size; + __u64 actual_size; + size_t policy_size; + union fscrypt_policy policy; + int err; + + if (get_user(size, &arg->size)) + return -EFAULT; + + if (size <= offsetof(struct fscrypt_get_policy_ex_args, policy) || + size >= 65536) return -EINVAL; - policy.version = 0; - policy.contents_encryption_mode = ctx.contents_encryption_mode; - policy.filenames_encryption_mode = ctx.filenames_encryption_mode; - policy.flags = ctx.flags; - memcpy(policy.master_key_descriptor, ctx.master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE); + err = fscrypt_get_policy(file_inode(filp), &policy); + if (err) + return err; + + policy_size = fscrypt_policy_size(&policy); + actual_size = offsetof(struct fscrypt_get_policy_ex_args, policy) + + policy_size; - if (copy_to_user(arg, &policy, sizeof(policy))) + if (size < actual_size) + return -EOVERFLOW; + + if (put_user(actual_size, &arg->size)) + return -EFAULT; + + if (copy_to_user(&arg->policy, &policy, policy_size)) return -EFAULT; return 0; } -EXPORT_SYMBOL(fscrypt_ioctl_get_policy); +EXPORT_SYMBOL(fscrypt_ioctl_get_policy_ex); /** * fscrypt_has_permitted_context() - is a file's encryption policy permitted @@ -155,10 +324,8 @@ EXPORT_SYMBOL(fscrypt_ioctl_get_policy); */ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { - const struct fscrypt_operations *cops = parent->i_sb->s_cop; - const struct fscrypt_info *parent_ci, *child_ci; - struct fscrypt_context parent_ctx, child_ctx; - int res; + union fscrypt_policy parent_policy, child_policy; + int err; /* No restrictions on file types which are never encrypted */ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && @@ -188,41 +355,22 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) * In any case, if an unexpected error occurs, fall back to "forbidden". */ - res = fscrypt_get_encryption_info(parent); - if (res) + err = fscrypt_get_encryption_info(parent); + if (err) return 0; - res = fscrypt_get_encryption_info(child); - if (res) + err = fscrypt_get_encryption_info(child); + if (err) return 0; - parent_ci = parent->i_crypt_info; - child_ci = child->i_crypt_info; - - if (parent_ci && child_ci) { - return memcmp(parent_ci->ci_master_key_descriptor, - child_ci->ci_master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ci->ci_data_mode == child_ci->ci_data_mode) && - (parent_ci->ci_filename_mode == - child_ci->ci_filename_mode) && - (parent_ci->ci_flags == child_ci->ci_flags); - } - res = cops->get_context(parent, &parent_ctx, sizeof(parent_ctx)); - if (res != sizeof(parent_ctx)) + err = fscrypt_get_policy(parent, &parent_policy); + if (err) return 0; - res = cops->get_context(child, &child_ctx, sizeof(child_ctx)); - if (res != sizeof(child_ctx)) + err = fscrypt_get_policy(child, &child_policy); + if (err) return 0; - return memcmp(parent_ctx.master_key_descriptor, - child_ctx.master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ctx.contents_encryption_mode == - child_ctx.contents_encryption_mode) && - (parent_ctx.filenames_encryption_mode == - child_ctx.filenames_encryption_mode) && - (parent_ctx.flags == child_ctx.flags); + return fscrypt_policies_equal(&parent_policy, &child_policy); } EXPORT_SYMBOL(fscrypt_has_permitted_context); @@ -238,30 +386,28 @@ EXPORT_SYMBOL(fscrypt_has_permitted_context); int fscrypt_inherit_context(struct inode *parent, struct inode *child, void *fs_data, bool preload) { - struct fscrypt_context ctx; - struct fscrypt_info *ci; - int res; + int err; + const struct fscrypt_info *ci; + union fscrypt_context ctx; - res = fscrypt_get_encryption_info(parent); - if (res < 0) - return res; + err = fscrypt_get_encryption_info(parent); + if (err) + return err; ci = parent->i_crypt_info; if (ci == NULL) return -ENOKEY; - ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - ctx.contents_encryption_mode = ci->ci_data_mode; - ctx.filenames_encryption_mode = ci->ci_filename_mode; - ctx.flags = ci->ci_flags; - memcpy(ctx.master_key_descriptor, ci->ci_master_key_descriptor, - FSCRYPT_KEY_DESCRIPTOR_SIZE); - get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE); - res = parent->i_sb->s_cop->set_context(child, &ctx, - sizeof(ctx), fs_data); - if (res) - return res; + + fscrypt_policy_to_context(&ci->ci_policy, &ctx); + + err = parent->i_sb->s_cop->set_context(child, &ctx, + fscrypt_context_size(&ctx), + fs_data); + if (err) + return err; + return preload ? fscrypt_get_encryption_info(child): 0; } EXPORT_SYMBOL(fscrypt_inherit_context); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 671ce57e4673..aa8c6e8bfed8 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -86,7 +86,7 @@ struct fscrypt_operations { }; /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ -#define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 +#define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 static inline bool fscrypt_dummy_context_enabled(struct inode *inode) { diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index bd60f951b06a..41bd5b70e343 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -70,6 +70,12 @@ static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) return -EOPNOTSUPP; } +static inline int fscrypt_ioctl_get_policy_ex(struct file *filp, + void __user *arg) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index ace278056dbe..a11b0b2d14b0 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -38,6 +38,7 @@ static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry) /* policy.c */ extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); extern int fscrypt_ioctl_get_policy(struct file *, void __user *); +extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *); extern int fscrypt_has_permitted_context(struct inode *, struct inode *); extern int fscrypt_inherit_context(struct inode *, struct inode *, void *, bool); From patchwork Mon Oct 23 21:40:50 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829667 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="JSDM4oG+"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJm4rdNz9sPr for ; Tue, 24 Oct 2017 08:43:36 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932330AbdJWVne (ORCPT ); Mon, 23 Oct 2017 17:43:34 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:56031 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932197AbdJWVmd (ORCPT ); Mon, 23 Oct 2017 17:42:33 -0400 Received: by mail-io0-f194.google.com with SMTP id p186so21681446ioe.12; Mon, 23 Oct 2017 14:42: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=M0HtKc8LygBEs38XxTPU0A68Tkp5KwzV3MolA/ydToc=; b=JSDM4oG+KMKrf4TmBa2GsGG3mxO97HfIXQ3mu0fJhuZ49/sd0twzvapIbyqpp0Xc/S E04ymH31jz8KHWsdatL++Zae2ajckexkTlJHNXZM5Cmqomgm2l5xkbqqAzyT9cPlGRXM Llbl4E8yT82ka0JD93ZKsfXXszLN5YHrzGiO0Jddjfym6rHeZGauoL/sT/j2AdTqVMqx ap7jBnGzt3n07hGD8oATQT3cF3Nuqd9szutf4Q3Xro+0u9Lj1MlqcBezHT1LR6jKh2xa 2PLGPTkJa744ldZSC+XDA80TcS6ORywNxe9plAcXI/JapyTBa2B2/D39QK2ph+6jltVg /MbQ== 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=M0HtKc8LygBEs38XxTPU0A68Tkp5KwzV3MolA/ydToc=; b=c56jMZxP7kLbXcbxTDWGkd0IgXeT6NE0xroFwD3QO7XEJVQEac28CICkNriE6r8NO9 0SGQCMjw5PAWdYP+jAAnNsT88xAla1DbL68IDPW5BbPncicIfz1WCULwV4Z07KuTjipe hkJ/P93ureO9+MXQ+KhnF2u1bd0wtXOTmvQKbWhcTNIf77Iy5Kkdgi1lgsYYLXPmlR6J H2Aypr0kK5HkfXu8rYlhRXBZ91Kr8L+FBNy3wItnvx2cpBnl6E2rFtuhMSAwuen4ZZK+ apSw6CEkXhQNYMqO3Imv/fgRS6Dd77dV/umsWdM/yJHRdILtqZE5CmSdyMExiMyVoJhd mjuA== X-Gm-Message-State: AMCzsaVidROgZWHGamiMGpux2r6P3f+qRYqkI1XdbTKUgd1JZf4dkEqr ypmwVbbcXzsYhe0PLl5e8dsWfv7G X-Google-Smtp-Source: ABhQp+T24jP6+ziED4xAPsPOQg/UCR1/Fn4arpEW41rz5dfWqUGeHyRIu4UO1LVYGcvli+UNRBQVCw== X-Received: by 10.107.3.193 with SMTP id e62mr19552820ioi.88.1508794952318; Mon, 23 Oct 2017 14:42:32 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:31 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 17/25] fscrypt: add an HKDF-SHA512 implementation Date: Mon, 23 Oct 2017 14:40:50 -0700 Message-Id: <20171023214058.128121-18-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers For v2 encryption policies, we need to hash the master key to compute a master_key_identifier. Naively, we could just use a truncated SHA-512 or another common cryptographic hash function. However, that would cause the same key material to be used in two different ways: both as input to the hash function, and as input to the AES-ECB-based KDF when deriving the per-inode encryption keys. There *probably* isn't any practical attack on that, but still it would be better "crypto hygiene" to use the key material for one purpose only, e.g. just for a KDF. It also happens that the AES-based KDF is on our list of things to fix. While it does generate unique derived keys with sufficient entropy, it is nonstandard and has some problems that don't exist in standard KDFs such as HKDF. For example, the AES-based KDF is reversible: given a derived key and nonce, an attacker can easily compute the master key. This was maybe okay for the original threat model of ext4 encryption where the master key and derived keys were considered equally hard to compromise. But now we would like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been wiped from memory via FS_IOC_REMOVE_ENCRYPTION_KEY. HKDF also has other advantages over the AES-ECB-based KDF such as evenly distributing the entropy from the input key material and being more extensible to deriving other key material that may be needed. Since we're introducing a new encryption policy version that already includes an on-disk format change, and we also now have a good place to cache an HMAC transform for each master key (struct fscrypt_master_key) so that HKDF can be implemented efficiently, we finally have a chance to switch to HKDF to derive the per-file keys. In addition, we'll use HKDF to derive the master_key_identifier, avoiding the need for a separate cryptographic hash primitive. This is secure because the output from HKDF is cryptographically isolated, i.e. sending some output in the clear doesn't reveal any other output, in a computational sense. (This is assuming that application-specific info strings aren't repeated between different uses of HKDF, but we'll use context bytes to ensure that.) Thus, this patch adds an implementation of HKDF to keyinfo.c, using an HMAC transform allocated from the crypto API. Later patches will make use of it. Note that using HKDF-SHA512 as the key derivation function will introduce a dependency on the security and implementation of SHA-512, whereas before we were using only AES for both key derivation and encryption. However, by using HMAC rather than the hash function directly, HKDF is designed to remain secure even if various classes of attacks, e.g. collision attacks, are found against the underlying unkeyed hash function. Even HMAC-MD5 is still considered secure in practice, despite MD5 itself having been heavily compromised. And meanwhile, the AES-based KDF used the public nonce as the cipher *key*, which is an unusual case which probably hasn't undergone much cryptanalysis. HKDF-SHA512 seems like a safer bet. We *could* actually avoid introducing a hash primitive by instantiating HKDF-Expand with CMAC-AES256 as the pseudorandom function rather than HMAC-SHA512. That would work; however, the HKDF specification doesn't explicitly allow a non-HMAC pseudorandom function, so it would be less standard. It would also require skipping HKDF-Extract and making the API accept only 32-byte master keys, since otherwise HKDF-Extract using CMAC-AES would produce a pseudorandom key only 16 bytes long which would only be enough for AES-128, not AES-256. References: - RFC 5869. "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)". https://tools.ietf.org/html/rfc5869 - Krawczyk (2010). "Cryptographic Extraction and Key Derivation: The HKDF Scheme". https://eprint.iacr.org/2010/264.pdf Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/keyinfo.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 02b7d91c9231..bbd4e38b293c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -8,6 +8,8 @@ config FS_ENCRYPTION select CRYPTO_CTS select CRYPTO_CTR select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 937a678ebba1..e9de625ddfe4 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -6,6 +6,11 @@ * This contains encryption key functions. * * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. + * + * HKDF support and key add/remove API added by Eric Biggers, 2017. + * + * The implementation and usage of HKDF should conform to RFC-5869 ("HMAC-based + * Extract-and-Expand Key Derivation Function"). */ #include @@ -14,10 +19,181 @@ #include #include #include +#include #include #include "fscrypt_private.h" -static struct crypto_shash *essiv_hash_tfm; +/* + * Any unkeyed cryptographic hash algorithm can be used with HKDF, but we use + * SHA-512 because it is reasonably secure and efficient; and since it produces + * a 64-byte digest, deriving an AES-256-XTS key preserves all 64 bytes of + * entropy from the master key and requires only one iteration of HKDF-Expand. + */ +#define HKDF_HMAC_ALG "hmac(sha512)" +#define HKDF_HASHLEN SHA512_DIGEST_SIZE + +/* + * HKDF consists of two steps: + * + * 1. HKDF-Extract: extract a fixed-length pseudorandom key from the + * input keying material and optional salt. + * 2. HKDF-Expand: expand the pseudorandom key into output keying material of + * any length, parameterized by an application-specific info string. + * + * HKDF-Extract can be skipped if the input is already a good pseudorandom key + * that is at least as long as the hash. While the fscrypt master keys should + * already be good pseudorandom keys, when using encryption algorithms that use + * short keys (e.g. AES-128-CBC) we'd like to permit the master key to be + * shorter than HKDF_HASHLEN bytes. Thus, we still must do HKDF-Extract. + * + * Ideally, HKDF-Extract would be passed a random salt for each distinct input + * key. Details about the advantages of a random salt can be found in the HKDF + * paper (Krawczyk, 2010; "Cryptographic Extraction and Key Derivation: The HKDF + * Scheme"). However, we do not have the ability to store a salt on a + * per-master-key basis. Thus, we have to use a fixed salt. This is sufficient + * as long as the master keys are already pseudorandom and are long enough to + * make dictionary attacks infeasible. This should be the case if userspace + * used a cryptographically secure random number generator, e.g. /dev/urandom, + * to generate the master keys and it was initialized with sufficient entropy. + * + * For the fixed salt we use "fscrypt_hkdf_salt" rather than default of all 0's + * defined by RFC-5869. This is only to be slightly more robust against + * userspace (unwisely) reusing the master keys for different purposes. + * Logically, it's more likely that the keys would be passed to unsalted + * HKDF-SHA512 than specifically to "fscrypt_hkdf_salt"-salted HKDF-SHA512. + * Of course, a random salt would be better for this purpose. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALT_LEN (sizeof(HKDF_SALT) - 1) + +/* + * HKDF-Extract (RFC-5869 section 2.2). This extracts a pseudorandom key 'prk' + * from the input key material 'ikm' and a salt. See explanation above for why + * we use a fixed salt. + */ +static int hkdf_extract(struct crypto_shash *hmac_tfm, + const u8 *ikm, unsigned int ikmlen, + u8 prk[HKDF_HASHLEN]) +{ + SHASH_DESC_ON_STACK(desc, hmac_tfm); + int err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALT_LEN); + if (err) + goto out; + + err = crypto_shash_digest(desc, ikm, ikmlen, prk); +out: + shash_desc_zero(desc); + return err; +} + +/* + * HKDF-Expand (RFC-5869 section 2.3). This expands the pseudorandom key, which + * has already been keyed into 'hmac_tfm', into 'okmlen' bytes of output keying + * material, parameterized by the application-specific information string of + * 'info' prefixed with the 'context' byte. ('context' isn't part of the HKDF + * specification; it's just a prefix we add to our application-specific info + * strings to guarantee that we don't accidentally repeat an info string when + * using HKDF for different purposes.) + */ +static int hkdf_expand(struct crypto_shash *hmac_tfm, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hmac_tfm); + int err; + const u8 *prev = NULL; + unsigned int i; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + desc->tfm = hmac_tfm; + desc->flags = 0; + + if (unlikely(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + for (i = 0; i < okmlen; i += HKDF_HASHLEN) { + + err = crypto_shash_init(desc); + if (err) + goto out; + + if (prev) { + err = crypto_shash_update(desc, prev, HKDF_HASHLEN); + if (err) + goto out; + } + + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + if (okmlen - i < HKDF_HASHLEN) { + err = crypto_shash_finup(desc, &counter, 1, tmp); + if (err) + goto out; + memcpy(&okm[i], tmp, okmlen - i); + memzero_explicit(tmp, sizeof(tmp)); + } else { + err = crypto_shash_finup(desc, &counter, 1, &okm[i]); + if (err) + goto out; + } + counter++; + prev = &okm[i]; + } + err = 0; +out: + shash_desc_zero(desc); + return err; +} + +/* + * Precompute HKDF-Extract using the master key as the input key material, then + * return an HMAC transform that is keyed using the resulting pseudorandom key. + * This can be used to derive further key material using HKDF-Expand. + */ +static struct crypto_shash *allocate_hmac_tfm(const u8 *master_key, u32 size) +{ + struct crypto_shash *hmac_tfm; + u8 prk[HKDF_HASHLEN]; + int err; + + hmac_tfm = crypto_alloc_shash(HKDF_HMAC_ALG, 0, 0); + if (IS_ERR(hmac_tfm)) { + pr_warn("fscrypt: error allocating " HKDF_HMAC_ALG ": %ld\n", + PTR_ERR(hmac_tfm)); + goto out; + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, size, prk); + if (err) + goto fail; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto fail; +out: + memzero_explicit(prk, sizeof(prk)); + return hmac_tfm; + +fail: + crypto_free_shash(hmac_tfm); + hmac_tfm = ERR_PTR(err); + goto out; +} /* * fscrypt_master_key_secret - secret key material of an in-use master key @@ -914,6 +1090,8 @@ static void put_crypt_info(struct fscrypt_info *ci) kmem_cache_free(fscrypt_info_cachep, ci); } +static struct crypto_shash *essiv_hash_tfm; + static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt) { struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm); From patchwork Mon Oct 23 21:40:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829665 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="GpF44tTH"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJj6B1Tz9sPr for ; Tue, 24 Oct 2017 08:43:33 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751432AbdJWVn2 (ORCPT ); Mon, 23 Oct 2017 17:43:28 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:56950 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932204AbdJWVmg (ORCPT ); Mon, 23 Oct 2017 17:42:36 -0400 Received: by mail-io0-f194.google.com with SMTP id m81so21686339ioi.13; Mon, 23 Oct 2017 14:42:34 -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=/NNjL42227y+17N3q+8JQJQx/GzyIdYSd/kkDs6iCQs=; b=GpF44tTHxdqPPFgMsO1/N/6A0Wx5/j5UD7xqdhn4ZUXV0h89Re7LAZBUlAykdJnQPu dzmRoA7lg77j4TZnUkdteJL52pOy5sbsPMig+H6xLWQNe1tDy1QqBz/PyWRTtMVQvBRz QVS28dNnifcLXJCmq5unqDB7aERIAeXu/J5ifm0K4mCZxpdRycN8Ca52o2Utj+5CEbs3 ZG8zg/FqEHiN7sqrsamcU56sIJodqjZrMEwYC1WtBSFfIQ96SBSx8UF17TCaU5qsW/ux KOOoyl2kDDJ6b41IoW9as1BMJCe1C1a6lGoszy7UtRGPqwYHB9hiJNRSoOEHVHY6zXyK 3vQg== 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=/NNjL42227y+17N3q+8JQJQx/GzyIdYSd/kkDs6iCQs=; b=ksunJ8OR1Nwy5pBuJE2EHVGao/H/YrYo6nlW78hwlqk069LYPqIDeePcs2I695VGH6 01FfOHzclQHQD2Ld7um+phFz/Nzk75mJmQOFC8PIICL9dtOsQNUKomsvUREsJDeUOnJV 2syTti2hSUfJ5UwYqmhkWl1kU/GBrOd/p4YxsFj3ZWmVBqUQooiCeqpM2thcJXn8kkQK l5XgrdnsAyGsuQuIZIBGKnh5MHvcDpbsQX8X/hDjshrA9rNyndP5AOUzYHMqQjuaDU0L dKBMWlDJ4pDChNBLEC0vjYfFFiK5wHVH2zK0grnEsBAP7KKRoO0+Xdd6kLIrC1mR3Hw4 /2XA== X-Gm-Message-State: AMCzsaUT2t1dtQ+3QInA3gbAmdJfoDpNKA+NXtfrzLs6+Q/Z/0pR734+ VOsKJhWoA5MB/Oo+3gzSEuKDPjIP X-Google-Smtp-Source: ABhQp+QO1HWbcnv37YJYKloe6q+/CzEmiUuOwCFgWmigS7UXdf47wc089dYilSAnkyQHjKUyZOAjLQ== X-Received: by 10.107.163.15 with SMTP id m15mr18627859ioe.61.1508794953630; Mon, 23 Oct 2017 14:42:33 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:33 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 18/25] fscrypt: allow adding and removing keys for v2 encryption policies Date: Mon, 23 Oct 2017 14:40:51 -0700 Message-Id: <20171023214058.128121-19-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Extend the FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY ioctls to support adding and removing keys for use by v2 encryption policies. Keys for v2 encryption policies are identified by a 16-byte "identifier", which is a cryptographic hash of the key, instead of by an 8-byte "descriptor". For FS_IOC_ADD_ENCRYPTION_KEY, the kernel calculates the key identifier and copies it to userspace. Userspace is not allowed to choose the key identifier, since the kernel would have to recalculate it anyway to verify that it is correct. For FS_IOC_REMOVE_ENCRYPTION_KEY, userspace provides the key identifier rather than the key descriptor to identify the key to be removed. For both ioctls, a type field indicates whether the old or new way of specifying keys is being used. A common structure is used, 'struct fscrypt_key_specifier', and it has some extra space just in case we have to introduce a new way to identify keys in the future. Note that keys for v1 and v2 encryption policies are both stored in ->s_master_keys, but their descriptions will be of different lengths. Therefore, they cannot be mixed up when we search for a key. For now the ioctls still always require capable(CAP_SYS_ADMIN). We'll be able to relax that soon, but only for v2 policies. Signed-off-by: Eric Biggers --- fs/crypto/keyinfo.c | 75 ++++++++++++++++++++++++++++++++++++++++---- include/uapi/linux/fscrypt.h | 2 ++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index e9de625ddfe4..c9e7b2b262d2 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -32,6 +32,19 @@ #define HKDF_HMAC_ALG "hmac(sha512)" #define HKDF_HASHLEN SHA512_DIGEST_SIZE +/* + * The list of contexts in which we use HKDF to derive additional keys from a + * master key. The values in this list are used as the first byte of the + * application-specific info string to guarantee that info strings are never + * repeated between contexts. + * + * Keys derived with different info strings are cryptographically isolated from + * each other --- knowledge of one derived key doesn't reveal any others. + * (This property is particularly important for the derived key used as the + * "key identifier", as that is stored in the clear.) + */ +#define HKDF_CONTEXT_KEY_IDENTIFIER 1 + /* * HKDF consists of two steps: * @@ -228,7 +241,12 @@ struct fscrypt_master_key { */ struct fscrypt_master_key_secret mk_secret; - /* Arbitrary key descriptor which was assigned by userspace */ + /* + * For v1 policy keys: an arbitrary key descriptor which was assigned by + * userspace (->descriptor). + * + * For v2 policy keys: a cryptographic hash of this key (->identifier). + */ struct fscrypt_key_specifier mk_spec; /* @@ -252,6 +270,8 @@ static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec) switch (spec->type) { case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: return FSCRYPT_KEY_DESCRIPTOR_SIZE; + case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER: + return FSCRYPT_KEY_IDENTIFIER_SIZE; } return 0; } @@ -346,7 +366,7 @@ static struct key *search_fscrypt_keyring(struct key *keyring, #define FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE \ (sizeof("fscrypt-") - 1 + sizeof(((struct super_block *)0)->s_id) + 1) -#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1) +#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1) static void format_fs_keyring_description( char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE], @@ -550,6 +570,39 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) if (copy_from_user(secret.raw, uarg->raw, secret.size)) goto out_wipe_secret; + if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + struct crypto_shash *hmac_tfm; + + hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size); + if (IS_ERR(hmac_tfm)) { + err = PTR_ERR(hmac_tfm); + goto out_wipe_secret; + } + + /* + * Hash the master key to get the key identifier, then return it + * to userspace. Specifically, we derive the key identifier + * from the master key with HKDF-SHA512, using a unique + * application-specific info string. This is preferable to a + * simple truncated SHA512 because it means we only ever use the + * master key for a single cryptographic primitive (HKDF-SHA512) + * rather than two (HKDF-SHA512 and SHA512). It *maybe* would + * be okay, but cryptographically it would be bad practice. + */ + err = hkdf_expand(hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER, + NULL, 0, arg.key_spec.identifier, + FSCRYPT_KEY_IDENTIFIER_SIZE); + crypto_free_shash(hmac_tfm); + if (err) + goto out_wipe_secret; + + err = -EFAULT; + if (copy_to_user(uarg->key_spec.identifier, + arg.key_spec.identifier, + FSCRYPT_KEY_IDENTIFIER_SIZE)) + goto out_wipe_secret; + } + err = add_master_key(sb, &secret, &arg.key_spec); out_wipe_secret: wipe_master_key_secret(&secret); @@ -872,6 +925,10 @@ static int derive_key_aes(const u8 *master_key, * Search the current task's subscribed keyrings for a "logon" key with * description prefix:descriptor, and if found acquire a read lock on it and * return a pointer to its validated payload in *payload_ret. + * + * This is only used for v1 encryption policies, where keys are identified by + * master_key_descriptor. With newer policy versions, only the filesystem-level + * keyring (->s_master_keys) is supported. */ static struct key * find_and_lock_process_key(const char *prefix, @@ -977,17 +1034,23 @@ static int find_and_derive_key(const struct inode *inode, memcpy(mk_spec.descriptor, ctx->v1.master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE); break; + case FSCRYPT_CONTEXT_V2: + mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + memcpy(mk_spec.identifier, ctx->v2.master_key_identifier, + FSCRYPT_KEY_IDENTIFIER_SIZE); + break; default: - return -EOPNOTSUPP; + return -EOPNOTSUPP; /* should have been checked earlier too */ } key = find_master_key(inode->i_sb, &mk_spec); if (IS_ERR(key)) { - if (key != ERR_PTR(-ENOKEY)) + if (key != ERR_PTR(-ENOKEY) || + ctx->v1.version != FSCRYPT_CONTEXT_V1) return PTR_ERR(key); /* - * As a legacy fallback, we search the current task's subscribed - * keyrings in addition to ->s_master_keys. + * As a legacy fallback for v1 policies, we search the current + * task's subscribed keyrings in addition to ->s_master_keys. */ return find_and_derive_key_legacy(inode, &ctx->v1, derived_key, derived_keysize); diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 065060b20a34..1918bdc0c6d7 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -82,10 +82,12 @@ struct fscrypt_get_policy_ex_args { struct fscrypt_key_specifier { __u32 type; #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 +#define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 __u32 reserved; union { __u8 max_specifier[32]; __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; }; }; From patchwork Mon Oct 23 21:40:52 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829666 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="IDXC3K9+"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJk57SCz9sPt for ; Tue, 24 Oct 2017 08:43:34 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932321AbdJWVnb (ORCPT ); Mon, 23 Oct 2017 17:43:31 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:51254 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932209AbdJWVmg (ORCPT ); Mon, 23 Oct 2017 17:42:36 -0400 Received: by mail-io0-f194.google.com with SMTP id b186so21740071iof.8; Mon, 23 Oct 2017 14:42:35 -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=AdQYfKtecEkjrPqGJc7GmeYZ5R0tQ7+mjuXrtXhv3cE=; b=IDXC3K9+77tXXXofmuOcNbeAwtevVmewKwsRAiB89YsrMQnckXwAgh8h6yX0XHIjXv DvXdjAawMLKqBrBkyFJLTRWNO+DSung/lZx10XSgGWgiuEQq+Cmi04GeEjFUfoWJRP/a XahJ70l0iL8b1NCt4UoS/Wpvk8fiNEnk/ILec0PVv/2Sc1Q1zU0PWkyDNOpMb+FTQkCY xKfOjHS9sh03tX7bSelLE3s8MDQ1SCpUHVaUmCfWTWgVCg/OdkVLTI28up7+kWzI47ER NHcW7W+KfwtZ5BOvt6Owzf4JcSjn7vLM/wUvWm9NBdTlNNSRKoCbtv1isg647gsJBNt0 xaww== 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=AdQYfKtecEkjrPqGJc7GmeYZ5R0tQ7+mjuXrtXhv3cE=; b=ge9Cy8CO/KEqKAZm3I8X/hn+gZl2TWAYQ5YTf+2skxI8h8WRkpgGAvb2zKfzMzX1dJ SEDprFEDgnjF5pF1/NfyRat5KR9fvvoD0/DE38hET2iDyG69DzXO8I4mZw1R2MWqodAb 6WoKkCFjpd4Q/shSL97hTgz7E6LS3zu6cjiRrdc/s6ZtoHhPj1UtjIS0wLitt7efKg5C YnOlUPKGX4fdoEPjOwUKfzKW+x7iejT+qxAgeDvWZqUXaSOypw5fji+FvQ9HRbgv4jH8 zK9pFcjqG5jXrbKBVmWa11rgWAVx/xcyXcbzY3RBmjwkqfGoTnN5EePGw5OYsF+ynaCR KLxg== X-Gm-Message-State: AMCzsaWWYwmfIgGuMTzcgnnYGYQy8n4rO+MbzJtY36TcBMk5jHdiWwu3 9w950FkfCj32FflCVuwMWHp/nL6P X-Google-Smtp-Source: ABhQp+TU54ewO3yKgl+t8uOEZ1bu2tAkcffEx2YRVffG1fWbzAl6jMAK2WFm/2JU3XlxAENl61l4tA== X-Received: by 10.107.132.167 with SMTP id o39mr19830735ioi.243.1508794954930; Mon, 23 Oct 2017 14:42:34 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:34 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 19/25] fscrypt: use HKDF-SHA512 to derive the per-file keys for v2 policies Date: Mon, 23 Oct 2017 14:40:52 -0700 Message-Id: <20171023214058.128121-20-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers The AES-ECB-based method we're using to derive the per-inode encryption keys is nonstandard and has a number of problems, such as being trivially reversible. Fix these problems for v2 encryption policies by deriving the keys using HKDF-SHA512 instead. The inode's nonce prefixed with a context byte is used as the application-specific info string. Supposedly, one of the reasons that HKDF wasn't used originally was because of performance concerns. However, we actually can derive on the order of 1 million keys per second, so it's likely not a bottleneck in practice. Moreover, although HKDF-SHA512 can require a bit more actual crypto work per key derivation than the old KDF, the real world performance is actually just as good or even better than the old KDF. This is because the old KDF has to allocate and key a new "ecb(aes)" transform for every key derivation (since it's keyed with the nonce rather than the master key), whereas with HKDF we simply use a cached, pre-keyed "hmac(sha512)" transform. And the old KDF often spends more time allocating its crypto transform than doing actual crypto work. Another benefit to switching to HKDF is that we no longer need to hold the raw master key in memory, but rather only an HMAC transform keyed by a pseudorandom key extracted from the master key. Of course, for the software HMAC implementation there is no security benefit, since compromising the state of the HMAC transform is equivalent to compromising the raw master key. However, there could be a security benefit if used with an HMAC implementation that holds the secret in crypto accelerator hardware rather than in main memory. Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 4 ++-- fs/crypto/keyinfo.c | 58 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index dec85c4b14a8..6d420c9a85bd 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -49,7 +49,7 @@ struct fscrypt_context_v1 { /* * A unique value used in combination with the master key to derive the - * file's actual encryption key + * file's actual encryption key, using the AES-ECB-based KDF. */ u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; }; @@ -78,7 +78,7 @@ struct fscrypt_context_v2 { /* * A unique value used in combination with the master key to derive the - * file's actual encryption key + * file's actual encryption key, using HKDF-SHA512. */ u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; }; diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index c9e7b2b262d2..ec181c4eca56 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -44,6 +44,7 @@ * "key identifier", as that is stored in the clear.) */ #define HKDF_CONTEXT_KEY_IDENTIFIER 1 +#define HKDF_CONTEXT_PER_FILE_KEY 2 /* * HKDF consists of two steps: @@ -213,10 +214,18 @@ static struct crypto_shash *allocate_hmac_tfm(const u8 *master_key, u32 size) */ struct fscrypt_master_key_secret { - /* Size of the raw key in bytes */ + /* + * For v2 policy keys: an HMAC transform keyed by the pseudorandom key + * generated by computing HKDF-Extract with the raw master key as the + * input key material. This is used to efficiently derive the per-inode + * encryption keys using HKDF-Expand later. + */ + struct crypto_shash *hmac_tfm; + + /* Size of the raw key in bytes. Set even if ->raw isn't set. */ u32 size; - /* The raw key */ + /* For v1 policy keys: the raw key. */ u8 raw[FSCRYPT_MAX_KEY_SIZE]; }; @@ -291,6 +300,7 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret) static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret) { + crypto_free_shash(secret->hmac_tfm); memzero_explicit(secret, sizeof(*secret)); } @@ -571,14 +581,17 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) goto out_wipe_secret; if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - struct crypto_shash *hmac_tfm; - hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size); - if (IS_ERR(hmac_tfm)) { - err = PTR_ERR(hmac_tfm); + secret.hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size); + if (IS_ERR(secret.hmac_tfm)) { + err = PTR_ERR(secret.hmac_tfm); + secret.hmac_tfm = NULL; goto out_wipe_secret; } + /* The raw key is no longer needed */ + memzero_explicit(secret.raw, sizeof(secret.raw)); + /* * Hash the master key to get the key identifier, then return it * to userspace. Specifically, we derive the key identifier @@ -589,10 +602,9 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) * rather than two (HKDF-SHA512 and SHA512). It *maybe* would * be okay, but cryptographically it would be bad practice. */ - err = hkdf_expand(hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER, + err = hkdf_expand(secret.hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0, arg.key_spec.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE); - crypto_free_shash(hmac_tfm); if (err) goto out_wipe_secret; @@ -871,8 +883,13 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) } /* - * Key derivation function. This generates the derived key by encrypting the - * master key with AES-128-ECB using the nonce as the AES key. + * Legacy key derivation function. This generates the derived key by encrypting + * the master key with AES-128-ECB using the nonce as the AES key. This + * provides a unique derived key with sufficient entropy for each inode. + * However, it's nonstandard, non-extensible, doesn't evenly distribute the + * entropy from the master key, and is trivially reversible: an attacker who + * compromises a derived key can "decrypt" it to get back to the master key, + * then derive any other key. For all new code, use HKDF instead. * * The master key must be at least as long as the derived key. If the master * key is longer, then only the first 'derived_keysize' bytes are used. @@ -1078,8 +1095,21 @@ static int find_and_derive_key(const struct inode *inode, goto out_release_key; } - err = derive_key_aes(mk->mk_secret.raw, &ctx->v1, - derived_key, derived_keysize); + /* + * Derive the inode's encryption key, given the master key and the nonce + * from the inode's fscrypt_context. v1 policies used an AES-ECB-based + * KDF (Key Derivation Function). Newer policies use HKDF-SHA512, which + * fixes a number of problems with the AES-ECB-based KDF. + */ + if (ctx->v1.version == FSCRYPT_CONTEXT_V1) { + err = derive_key_aes(mk->mk_secret.raw, &ctx->v1, + derived_key, derived_keysize); + } else { + err = hkdf_expand(mk->mk_secret.hmac_tfm, + HKDF_CONTEXT_PER_FILE_KEY, + ctx->v2.nonce, sizeof(ctx->v2.nonce), + derived_key, derived_keysize); + } if (err) goto out_release_key; @@ -1275,8 +1305,8 @@ int fscrypt_get_encryption_info(struct inode *inode) goto out; /* - * This cannot be a stack buffer because it is passed to the scatterlist - * crypto API as part of key derivation. + * This cannot be a stack buffer because it may be passed to the + * scatterlist crypto API during key derivation. */ res = -ENOMEM; derived_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); From patchwork Mon Oct 23 21:40:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829663 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="IukNUpLi"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJL6t6Pz9sPt for ; Tue, 24 Oct 2017 08:43:14 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932304AbdJWVnM (ORCPT ); Mon, 23 Oct 2017 17:43:12 -0400 Received: from mail-io0-f195.google.com ([209.85.223.195]:49670 "EHLO mail-io0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932217AbdJWVmi (ORCPT ); Mon, 23 Oct 2017 17:42:38 -0400 Received: by mail-io0-f195.google.com with SMTP id n137so21761540iod.6; Mon, 23 Oct 2017 14:42:37 -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=UqwBPntNJGFGHEFPhT53FrUJ2990XYmdEL0ehuffT9Q=; b=IukNUpLiORswWOOlNVsNHalZYjulIr1cEtDZHqweAqMsvKVh70F4DX0eGvrnlcOM8N ZCCdPtmn827kOGSi2TZpK2hxx7jEZbrKVJZVZoJeLH9zFHi3Nx2BdyCIcUrRoQ/CSgCS 5lxhKbJj5pl7dtci0zZ9dPmvK4sEENm1Ri5YruZ+bqn5lHDu2ITge4nIsiaI0+knBB9D BbryWBODS4CA7NnxZQg9l+CpL19V6Z0GXgug+awapQfsOnzh6YpCpYbqXJ+TGLpSKaE4 F9nM16R7QoB2kK0+8n9TEisLSxDq3Gdmb8V8wya9Jvu/aZntgEivyYXHhaAj6rLqdxOg LYSQ== 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=UqwBPntNJGFGHEFPhT53FrUJ2990XYmdEL0ehuffT9Q=; b=OVs+I0walvz4bSiPIBPJf2cscKBFZnu7eIJvbmvrWflhw16yAZlRfdbVoTeDkYlnEo KHrEEDwKijYLQr6GFr2a3lRshajsBAmUhn5AJYADyv54PH497j/hFpqNUzabXpNYqNl4 PUVT8XiCoK390ZGRuB4DL2eDAk8sSS50MdklZfOYWxEKT2aZADkvlWd1W6aYr2pLQmUb 6ST9zE4C3e2IHzfvpkwWkodiDa7vf5FpLNeye4LF/Drwvn/ke0GK+wPmTEbbRvGcxTJH PvnSA+9jG9L/k82xGIT5BhQ+A2whA3wDdm1CJi0oqMpOFTdbNkjKCv6v4XroNofos6y5 I7iw== X-Gm-Message-State: AMCzsaWLX9pd6RvEe/kMNLeuLXjrGr1/ixMJ6KkSUx+Qoa8Ly+UDqgsx RzfaF260TGWH0O0RJt/j2/4RNiPC X-Google-Smtp-Source: ABhQp+QyB7tPiBxn4LPdKlp3Q3yiZjqScE13KNDymmZrNUEb0UbB9nAoWKAtYLG3S7Qc6HZc6CNTBQ== X-Received: by 10.107.147.135 with SMTP id v129mr17984940iod.289.1508794956272; Mon, 23 Oct 2017 14:42:36 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:35 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 20/25] fscrypt: allow unprivileged users to add/remove keys for v2 policies Date: Mon, 23 Oct 2017 14:40:53 -0700 Message-Id: <20171023214058.128121-21-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Having the FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY ioctls be root-only is sufficient for some users of filesystem encryption, e.g. Android and Chromium OS where all the encryption keys are managed by a privileged process. However, it is not sufficient for general use where non-root users are setting up encrypted directories. If these ioctls were root-only, such users would have to continue to use process-subscribed keyrings and would continue to run into all the problems noted earlier, including visibility problems when processes running under different UIDs need to be able to access the files, and the inability to remove the key, "locking" the directory. Fortunately, we can indeed make the ioctls unprivileged, but only for v2 encryption policies and only after a few additional changes which this patch implements. First, to allow any user to add a key with filesystem-level visibility, the keys must be identified using a cryptographic hash so that users cannot add the wrong key for other users' files. We use the key_identifier for this, which is why v2 encryption policies are a requirement. Second, we charge each key a user adds to their quota for the keyrings service. Thus, a user can't cause a denial of service by adding a very large number of keys. Third, we have to be careful about when a key is allowed to be removed, given that multiple users may add the same key (although that should *not* normally be the case; it's astronomically unlikely for keys to collide by chance, so it should only happen as a result of explicit sharing or compromise). One might consider only allowing the first user who added a key to remove it, or allowing any user who knows a key to remove it. But neither of those are good enough because we don't want a user on the system who knows another user's key to be able to cause a denial of service where the former user removes the latter user's key at an inopportune time. After all, it *should* be the case that if you have an encrypted directory and you give everyone in the world the key, including malicious users on the same system, it should still be no less secure than *not* using encryption. The solution is to keep track of which users have added a key and only really remove the key once all users have removed it. However, it is tolerated that a user will be unable to remove a key, i.e. unable to "lock" their encrypted files, if another user has added the same key. But in a sense, this is actually a good thing because it will avoid providing a false notion of security where a key appears to have been "removed" when actually it's still in memory, available to any attacker who compromises the operating system kernel. Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 7 + fs/crypto/fscrypt_private.h | 1 + fs/crypto/keyinfo.c | 341 ++++++++++++++++++++++++++++++++++++++++--- include/uapi/linux/fscrypt.h | 11 +- 4 files changed, 338 insertions(+), 22 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 489c504ac20d..e57a13889689 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -469,8 +469,14 @@ static int __init fscrypt_init(void) if (err) goto fail_free_info; + err = register_key_type(&key_type_fscrypt_mk_user); + if (err) + goto fail_unregister_fscrypt_mk_type; + return 0; +fail_unregister_fscrypt_mk_type: + unregister_key_type(&key_type_fscrypt_mk); fail_free_info: kmem_cache_destroy(fscrypt_info_cachep); fail_free_ctx: @@ -494,6 +500,7 @@ static void __exit fscrypt_exit(void) kmem_cache_destroy(fscrypt_ctx_cachep); kmem_cache_destroy(fscrypt_info_cachep); unregister_key_type(&key_type_fscrypt_mk); + unregister_key_type(&key_type_fscrypt_mk_user); fscrypt_essiv_cleanup(); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 6d420c9a85bd..d0a63086fa95 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -235,6 +235,7 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, /* keyinfo.c */ extern struct key_type key_type_fscrypt_mk; +extern struct key_type key_type_fscrypt_mk_user; extern void __exit fscrypt_essiv_cleanup(void); /* policy.c */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index ec181c4eca56..1fe44983239a 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -246,9 +246,16 @@ struct fscrypt_master_key { * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. * - * Locking: protected by key->sem. + * Locking: protected by key->sem (outer) and mk_secret_sem (inner). + * The reason for two locks is that key->sem also protects modifying + * mk_users, which ranks it above the semaphore for the keyring key + * type, which is in turn above page faults (via keyring_read). But + * sometimes filesystems call fscrypt_get_encryption_info() from within + * a transaction, which ranks it below page faults. So we need a + * separate lock which protects *just* mk_secret, not also mk_users. */ struct fscrypt_master_key_secret mk_secret; + struct rw_semaphore mk_secret_sem; /* * For v1 policy keys: an arbitrary key descriptor which was assigned by @@ -258,6 +265,19 @@ struct fscrypt_master_key { */ struct fscrypt_key_specifier mk_spec; + /* + * Keyring which contains a key of type 'key_type_fscrypt_mk_user' for + * each user who has added this key. Normally there would be just one + * user who adds a particular key, but it's possible that multiple users + * would add the same key, and we don't want to allow one user to remove + * it before the others want it removed too. + * + * This is NULL for v1 policy keys. + * + * Locking: protected by key->sem. + */ + struct key *mk_users; + /* * Length of ->mk_decrypted_inodes, plus one if mk_secret is present. * Once this goes to 0, the master key is removed from ->s_master_keys. @@ -314,6 +334,7 @@ static void move_master_key_secret(struct fscrypt_master_key_secret *dst, static void free_master_key(struct fscrypt_master_key *mk) { wipe_master_key_secret(&mk->mk_secret); + key_put(mk->mk_users); kzfree(mk); } @@ -353,9 +374,42 @@ struct key_type key_type_fscrypt_mk = { .describe = fscrypt_key_describe, }; +static int fscrypt_mk_user_key_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + /* + * We just charge FSCRYPT_MAX_KEY_SIZE bytes to the user's key quota for + * each key, regardless of the exact key size. The amount of memory + * actually used is greater than the size of the raw key anyway. + */ + return key_payload_reserve(key, FSCRYPT_MAX_KEY_SIZE); +} + +static void fscrypt_mk_user_key_describe(const struct key *key, + struct seq_file *m) +{ + seq_puts(m, key->description); +} + +/* + * Type of key in ->mk_users. Each key of this type represents a particular + * user who has added a particular master key. + * + * Note that the name of this key type really should be something like + * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen + * mainly for simplicity of presentation in /proc/keys when read by a non-root + * user. And it is expected to be rare that a key is actually added by multiple + * users, since users should keep their encryption keys confidential. + */ +struct key_type key_type_fscrypt_mk_user = { + .name = ".fscrypt", + .instantiate = fscrypt_mk_user_key_instantiate, + .describe = fscrypt_mk_user_key_describe, +}; + /* - * Search ->s_master_keys. Note that we mark the keyring reference as - * "possessed" so that we can use the KEY_POS_SEARCH permission. + * Search either ->s_master_keys or ->mk_users. Note that we mark the keyring + * reference as "possessed" so that we can use the KEY_POS_SEARCH permission. */ static struct key *search_fscrypt_keyring(struct key *keyring, struct key_type *type, @@ -378,6 +432,13 @@ static struct key *search_fscrypt_keyring(struct key *keyring, #define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1) +#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ + (sizeof("fscrypt-") - 1 + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ + sizeof("-users") - 1 + 1) + +#define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ + (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + sizeof(".uid.") - 1 + 10 + 1) + static void format_fs_keyring_description( char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE], const struct super_block *sb) @@ -393,6 +454,23 @@ static void format_mk_description( master_key_spec_len(mk_spec), mk_spec->max_specifier); } +static void format_mk_users_keyring_description( + char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], + const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) +{ + sprintf(description, "fscrypt-%*phN-users", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); +} + +static void format_mk_user_description( + char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], + const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) +{ + + sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, + mk_identifier, __kuid_val(current_fsuid())); +} + /* * Find the specified master key in ->s_master_keys. * Returns ERR_PTR(-ENOKEY) if not found. @@ -413,6 +491,80 @@ static struct key *find_master_key(struct super_block *sb, description); } +/* + * Find the current user's key in the master key's ->mk_users. + * Returns ERR_PTR(-ENOKEY) if not found. + */ +static struct key *find_master_key_user(struct fscrypt_master_key *mk) +{ + char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; + + format_mk_user_description(description, mk->mk_spec.identifier); + return search_fscrypt_keyring(mk->mk_users, &key_type_fscrypt_mk_user, + description); +} + +static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) +{ + char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; + struct key *mk_users; + + format_mk_users_keyring_description(description, + mk->mk_spec.identifier); + + mk_users = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, + current_cred(), KEY_POS_SEARCH | + KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(mk_users)) + return PTR_ERR(mk_users); + + mk->mk_users = mk_users; + return 0; +} + +/* + * Give the current user a key in ->mk_users. This charges the user's quota and + * marks the master key as added by the current user, so that it cannot be + * removed by another user with the key. + */ +static int add_master_key_user(struct fscrypt_master_key *mk) +{ + char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; + struct key *mk_user; + int err; + + format_mk_user_description(description, mk->mk_spec.identifier); + mk_user = key_alloc(&key_type_fscrypt_mk_user, description, + current_fsuid(), current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(mk_user)) + return PTR_ERR(mk_user); + + err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); + key_put(mk_user); + return err; +} + +/* + * Remove the current user's key from ->mk_users, if present. + */ +static int remove_master_key_user(struct fscrypt_master_key *mk) +{ + struct key *mk_user; + int err; + + mk_user = find_master_key_user(mk); + if (IS_ERR(mk_user)) { + if (mk_user != ERR_PTR(-ENOKEY)) + return PTR_ERR(mk_user); + return 0; + } + err = key_unlink(mk->mk_users, mk_user); + key_put(mk_user); + return err; +} + static struct key * allocate_master_key(struct fscrypt_master_key_secret *secret, const struct fscrypt_key_specifier *mk_spec) @@ -429,16 +581,36 @@ allocate_master_key(struct fscrypt_master_key_secret *secret, mk->mk_spec = *mk_spec; move_master_key_secret(&mk->mk_secret, secret); + init_rwsem(&mk->mk_secret_sem); refcount_set(&mk->mk_refcount, 1); /* secret is present */ INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); + if (mk_spec->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR) { + err = allocate_master_key_users_keyring(mk); + if (err) { + key = ERR_PTR(err); + goto out_free_mk; + } + + err = add_master_key_user(mk); + if (err) { + key = ERR_PTR(err); + goto out_free_mk; + } + } + + /* + * Note that we don't charge this key to anyone's quota since it's owned + * by root, and the keys in ->mk_users are charged instead. + */ format_mk_description(description, mk_spec); key = key_alloc(&key_type_fscrypt_mk, description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), KEY_POS_SEARCH | KEY_USR_SEARCH | - KEY_USR_READ | KEY_USR_VIEW, 0, NULL); + KEY_USR_READ | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) goto out_free_mk; @@ -510,12 +682,31 @@ static int add_master_key(struct super_block *sb, goto out_put_key; } else { struct fscrypt_master_key *mk = key->payload.data[0]; + struct key *mk_user; bool rekey; /* Found the key in ->s_master_keys */ down_write(&key->sem); + /* + * If the current user is already in ->mk_users, then there's + * nothing to do. + */ + if (mk->mk_users) { + mk_user = find_master_key_user(mk); + if (mk_user != ERR_PTR(-ENOKEY)) { + up_write(&key->sem); + if (IS_ERR(mk_user)) { + err = PTR_ERR(mk_user); + } else { + key_put(mk_user); + err = 0; + } + goto out_put_key; + } + } + /* * Take a reference if we'll be re-adding ->mk_secret. If we * couldn't take a reference, then the key is being removed from @@ -531,9 +722,24 @@ static int add_master_key(struct super_block *sb, goto retry; } + /* Add the current user to ->mk_users */ + if (mk->mk_users) { + err = add_master_key_user(mk); + if (err) { + up_write(&key->sem); + if (rekey && + refcount_dec_and_test(&mk->mk_refcount)) + key_invalidate(key); + goto out_put_key; + } + } + /* Re-add the secret key material if needed */ - if (rekey) + if (rekey) { + down_write(&mk->mk_secret_sem); move_master_key_secret(&mk->mk_secret, secret); + up_write(&mk->mk_secret_sem); + } up_write(&key->sem); } err = 0; @@ -548,6 +754,23 @@ static int add_master_key(struct super_block *sb, * Add a master encryption key to the filesystem, causing all files which were * encrypted with it to appear "unlocked" (decrypted) when accessed. The key * can be removed later by FS_IOC_REMOVE_ENCRYPTION_KEY. + * + * When adding a key for use by v1 encryption policies, this ioctl is + * privileged, and userspace must provide the 'key_descriptor'. + * + * When adding a key for use by v2+ encryption policies, this ioctl is + * unprivileged. This is needed, in general, to allow non-root users to use + * encryption without encountering the visibility problems of process-subscribed + * keyrings and the inability to properly remove keys. This works by having + * each key identified by its cryptographically secure hash --- the + * 'key_identifier'. The cryptographic hash ensures that a malicious user + * cannot add the wrong key for a given identifier. Furthermore, each added key + * is charged to the appropriate user's quota for the keyrings service, which + * prevents a malicious user from adding too many keys. Finally, we forbid a + * user from removing a key while other users have added it too, which prevents + * a user who knows another user's key from causing a denial-of-service by + * removing it at an inopportune time. (We tolerate that a user who knows a key + * can prevent other users from removing it.) */ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) { @@ -571,9 +794,6 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) if (!valid_key_spec(&arg.key_spec)) return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - memset(&secret, 0, sizeof(secret)); secret.size = arg.raw_size; err = -EFAULT; @@ -613,6 +833,15 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) arg.key_spec.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE)) goto out_wipe_secret; + } else { + /* + * Only root can add keys that are identified by an arbitrary + * descriptor rather than by a cryptographic hash --- since + * otherwise a malicious user could add the wrong key. + */ + err = -EACCES; + if (!capable(CAP_SYS_ADMIN)) + goto out_wipe_secret; } err = add_master_key(sb, &secret, &arg.key_spec); @@ -744,7 +973,9 @@ static int try_to_lock_encrypted_files(struct super_block *sb, } /* - * Try to remove an fscrypt master encryption key. + * Try to remove an fscrypt master encryption key. If other users have also + * added the key, we'll remove the current user's usage of the key, then return + * -EUSERS. Otherwise we'll continue on and try to actually remove the key. * * First we wipe the actual master key secret from memory, so that no more * inodes can be unlocked with it. Then, we try to evict all cached inodes that @@ -775,13 +1006,33 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg) if (copy_from_user(&arg, uarg, sizeof(arg))) return -EFAULT; - if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved))) + if (arg.flags & ~FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS) + return -EINVAL; + + if (arg.reserved1) + return -EINVAL; + + if (memchr_inv(arg.reserved2, 0, sizeof(arg.reserved2))) return -EINVAL; if (!valid_key_spec(&arg.key_spec)) return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) + /* + * Only root can request that the key be removed no matter how many + * user(s) have added it. + */ + if ((arg.flags & FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS) && + !capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* + * Only root can remove keys that are identified by an arbitrary + * descriptor rather than by a cryptographic hash --- since only root + * can add such keys. + */ + if (arg.key_spec.type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER && + !capable(CAP_SYS_ADMIN)) return -EACCES; key = find_master_key(sb, &arg.key_spec); @@ -790,10 +1041,34 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg) mk = key->payload.data[0]; down_write(&key->sem); + + if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (arg.flags & FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS) + err = keyring_clear(mk->mk_users); + else + err = remove_master_key_user(mk); + if (err) { + up_write(&key->sem); + goto out_put_key; + } + if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + /* + * Other users have still added the key too. We removed + * the current user's usage of the key if there was one, + * but we still can't remove the key itself. + */ + err = -EUSERS; + up_write(&key->sem); + goto out_put_key; + } + } + dead = false; if (is_master_key_secret_present(&mk->mk_secret)) { + down_write(&mk->mk_secret_sem); wipe_master_key_secret(&mk->mk_secret); dead = refcount_dec_and_test(&mk->mk_refcount); + up_write(&mk->mk_secret_sem); } up_write(&key->sem); if (dead) { @@ -802,6 +1077,7 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg) } else { err = try_to_lock_encrypted_files(sb, mk); } +out_put_key: key_put(key); return err; } @@ -818,6 +1094,15 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key); * regular file in it (which can confuse the "incompletely removed" state with * absent or present). * + * In addition, for v2 policy keys we allow applications to determine, via + * ->status_flags and ->user_count, whether the key has been added by the + * current user, by other users, or by both. Most applications should not need + * this, since ordinarily only one user should know a given key. However, if a + * secret key is shared by multiple users, applications may wish to add an + * already-present key to prevent other users from removing it. This ioctl can + * be used to check whether that really is the case before the work is done to + * add the key --- which might e.g. require prompting the user for a passphrase. + * * Note: this ioctl only works with keys added to the filesystem-level keyring. * It does *not* work with keys added via the old mechanism which involved * process-subscribed keyrings. @@ -839,6 +1124,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) if (!valid_key_spec(&arg.key_spec)) return -EINVAL; + arg.status_flags = 0; + arg.user_count = 0; arg.reserved2 = 0; memset(arg.reserved3, 0, sizeof(arg.reserved3)); @@ -860,6 +1147,19 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } arg.status = FSCRYPT_KEY_STATUS_PRESENT; + if (mk->mk_users) { + struct key *mk_user; + + arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; + mk_user = find_master_key_user(mk); + if (!IS_ERR(mk_user)) { + arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; + key_put(mk_user); + } else if (mk_user != ERR_PTR(-ENOKEY)) { + err = PTR_ERR(mk_user); + goto out_release_key; + } + } err = 0; out_release_key: up_read(&key->sem); @@ -1029,11 +1329,12 @@ static int find_and_derive_key_legacy(const struct inode *inode, * Find the master key, then derive the inode's actual encryption key. * * If the master key is found in the filesystem-level keyring, then the - * corresponding 'struct key' is returned read-locked in *master_key_ret. This - * is needed because we need to hold the semaphore until we link the new - * fscrypt_info into ->mk_decrypted_inodes, but in the case where multiple tasks - * are racing to set up an inode's ->i_crypt_info, only the winner should link - * its fscrypt_info into ->mk_decrypted_inodes. + * corresponding 'struct key' is returned in *master_key_ret with + * ->mk_secret_sem read-locked. This is needed because we need to hold + * ->mk_secret_sem until we link the new fscrypt_info into + * ->mk_decrypted_inodes, but in the case where multiple tasks are racing to set + * up an inode's ->i_crypt_info, only the winner should link its fscrypt_info + * into ->mk_decrypted_inodes. */ static int find_and_derive_key(const struct inode *inode, const union fscrypt_context *ctx, @@ -1073,7 +1374,7 @@ static int find_and_derive_key(const struct inode *inode, derived_keysize); } mk = key->payload.data[0]; - down_read(&key->sem); + down_read(&mk->mk_secret_sem); /* Has the secret been removed using FS_IOC_REMOVE_ENCRYPTION_KEY? */ if (!is_master_key_secret_present(&mk->mk_secret)) { @@ -1117,7 +1418,7 @@ static int find_and_derive_key(const struct inode *inode, return 0; out_release_key: - up_read(&key->sem); + up_read(&mk->mk_secret_sem); key_put(key); return err; } @@ -1361,7 +1662,9 @@ int fscrypt_get_encryption_info(struct inode *inode) } out: if (master_key) { - up_read(&master_key->sem); + struct fscrypt_master_key *mk = master_key->payload.data[0]; + + up_read(&mk->mk_secret_sem); key_put(master_key); } if (res == -ENOKEY) diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 1918bdc0c6d7..901e87d8fb74 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -102,7 +102,10 @@ struct fscrypt_add_key_args { /* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */ struct fscrypt_remove_key_args { - __u64 reserved[3]; + __u32 flags; +#define FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS 0x00000001 + __u32 reserved1; + __u64 reserved2[2]; struct fscrypt_key_specifier key_spec; }; @@ -117,9 +120,11 @@ struct fscrypt_get_key_status_args { #define FSCRYPT_KEY_STATUS_ABSENT 1 #define FSCRYPT_KEY_STATUS_PRESENT 2 #define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 + __u32 status_flags; +#define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 + __u32 user_count; __u32 reserved2; - - __u64 reserved3[7]; + __u64 reserved3[6]; }; #define FS_IOC_SET_ENCRYPTION_POLICY _IOR( 'f', 19, struct fscrypt_policy) From patchwork Mon Oct 23 21:40:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829662 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="mOtC1K5u"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJG4s69z9sPt for ; Tue, 24 Oct 2017 08:43:10 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932301AbdJWVnJ (ORCPT ); Mon, 23 Oct 2017 17:43:09 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:50629 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932221AbdJWVmi (ORCPT ); Mon, 23 Oct 2017 17:42:38 -0400 Received: by mail-io0-f194.google.com with SMTP id 97so21701422iok.7; Mon, 23 Oct 2017 14:42:38 -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=6bj+3PpKOEqnjjCfFytXcJdfqep6Tsq3tGn/CH2pnfU=; b=mOtC1K5uD1IQOUkDxmkg8LOeQg964c87AtZbg/MnR72YOTSWhPNgiIvWAoP3E5A3gy Xj7xm0j9Isa07F6HzOZyvz5v3fWGtqI1FBF+O/TLub2msFrJno9ZwtLb3KKmnycQl3gV eYcQEPqY2MXArBUjPt5WdiM89gCVnD/zqdx4ru8hotZqPhr3NOVBEFOLpDRTr7ELotIn alOkLbiBr96OnqNlsVXE4CMQm85lEFKz8HCPi6UsJzej6n5uxNGQSQq343Bs1VZqVXw9 b5nOiyyXdsTMkF4r2YqUC2P4RdDZBrg2A2ZPq11/P9L3b8wUfy9/jaF0rIWyFxF6Cvf2 qz4g== 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=6bj+3PpKOEqnjjCfFytXcJdfqep6Tsq3tGn/CH2pnfU=; b=KtZt6fN73fOAkJFKl3FCBCYB/ZpHFR5kbVapSXrj/E9QOVKjLC8NSoMFIN/EXI0T8y uAyulXInVevLPX6b6hbLWc5v1saMYmEFjoM3xEa8w3+ncCA4Wly1TSNZ4l+MYIU02QqF Rp70jksjinMLZ6tfdfLWhg7cRUHnNzZVQzEqLbo/4rX4gW7m6ykil8ZQDVVqGAmW7lEd 6lVA+9sfywueBBocEnJGhYENAUAB+ahPn1JefPrrRdORqmHiGoLUQ50xcqkffBkreQut 3hp4MrX5xWHfTJ/jkC9pxOLxXACgvPtZHGC9eyiVM8fAIf8sHHYXIX+3tvyOFWg6YNFq d3cg== X-Gm-Message-State: AMCzsaX1rD0KqIVQd0aGulQTjgtG0nXOjSIaEAr5GxUXlqCPlDodCY4K QOVW4Nhf0r9P5zxiMoI2AuRw5OHw X-Google-Smtp-Source: ABhQp+RH1Lyy/9yjcppo1NO0unSUK8en9i9rBXkfes1gjNC2HRCS9wOoUhBH+z8Uj6NbiIiqVyf2dw== X-Received: by 10.107.10.82 with SMTP id u79mr19424303ioi.252.1508794957596; Mon, 23 Oct 2017 14:42:37 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:37 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 21/25] fscrypt: require that key be added when setting a v2 encryption policy Date: Mon, 23 Oct 2017 14:40:54 -0700 Message-Id: <20171023214058.128121-22-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers By looking up the master keys in a filesystem-level keyring rather than in the calling processes' key hierarchy, it becomes possible for a user to set an encryption policy which refers to some key they don't actually know, then encrypt their files using that key. Cryptographically this shouldn't actually be a major problem; for one, every file will still be encrypted with a unique derived key, rather than with the master key directly. But to be on the safe side, enforce that a v2 encryption policy can only be set if the user has previously added the key, or has capable(CAP_FOWNER). We tolerate that this problem will continue to exist for v1 encryption policies, however; there is no way around that. Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 2 ++ fs/crypto/keyinfo.c | 42 ++++++++++++++++++++++++++++++++++++++++++ fs/crypto/policy.c | 6 ++++++ 3 files changed, 50 insertions(+) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index d0a63086fa95..7a0d5b6c2504 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -234,6 +234,8 @@ extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); /* keyinfo.c */ +extern int fscrypt_verify_key_added(struct super_block *sb, + const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]); extern struct key_type key_type_fscrypt_mk; extern struct key_type key_type_fscrypt_mk_user; extern void __exit fscrypt_essiv_cleanup(void); diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 1fe44983239a..fd59f37dad10 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -851,6 +851,48 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) } EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); +/* + * Verify that the current user has added a master key that has the given + * identifier (returns -ENOKEY if not). This is needed to prevent a user from + * encrypting their files using some other user's key which they don't actually + * know. Cryptographically speaking, it's debatable how much of a problem this + * actually would be, but it's best to just forbid it. + * + * The system administrator (CAP_FOWNER) can override this, which should be + * enough for any use cases where encryption policies are being set using keys + * that were chosen ahead of time but aren't available at the moment. + */ +int fscrypt_verify_key_added(struct super_block *sb, + const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) +{ + struct fscrypt_key_specifier mk_spec; + struct key *key, *mk_user; + struct fscrypt_master_key *mk; + int err; + + mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + memcpy(mk_spec.identifier, identifier, FSCRYPT_KEY_IDENTIFIER_SIZE); + + key = find_master_key(sb, &mk_spec); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto out; + } + mk = key->payload.data[0]; + mk_user = find_master_key_user(mk); + if (IS_ERR(mk_user)) { + err = PTR_ERR(mk_user); + } else { + key_put(mk_user); + err = 0; + } + key_put(key); +out: + if (err == -ENOKEY && capable(CAP_FOWNER)) + err = 0; + return err; +} + static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk) { struct fscrypt_info *ci; diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 27a391038f73..cfb404def9ed 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -170,6 +170,7 @@ static int set_encryption_policy(struct inode *inode, const union fscrypt_policy *policy) { union fscrypt_context ctx; + int err; if (!fscrypt_supported_policy(policy)) return -EINVAL; @@ -190,6 +191,11 @@ static int set_encryption_policy(struct inode *inode, */ pr_warn_once("%s (pid %d) is setting less secure v1 encryption policy; recommend upgrading to v2.\n", current->comm, current->pid); + } else { + err = fscrypt_verify_key_added(inode->i_sb, + policy->v2.master_key_identifier); + if (err) + return err; } return inode->i_sb->s_cop->set_context(inode, &ctx, From patchwork Mon Oct 23 21:40:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829661 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ghlbmXXM"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJG0FhMz9sPm for ; Tue, 24 Oct 2017 08:43:10 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932290AbdJWVnF (ORCPT ); Mon, 23 Oct 2017 17:43:05 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:53682 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932230AbdJWVmj (ORCPT ); Mon, 23 Oct 2017 17:42:39 -0400 Received: by mail-io0-f194.google.com with SMTP id 189so21720447iow.10; Mon, 23 Oct 2017 14:42:39 -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=3Yfs/8DKKdrc1dp/8fpKhalhfGjGCXihEUBk0IQ0evE=; b=ghlbmXXMFNKK2OeKK/bzMJgUsAkaXoq1wMkMXlFJDOvvYcMtA2Vb7XGDnfPo8OsjxJ 0iv8jAxJWQsll3arUb6se1Po7pv0yYI0TX9gCzaaKq2ClK5ymFvgfRY/GW6kcqb3g/Y/ xCJ30UgQYZDBYy0NNmbkMKjPm5zmdRIe6vVMPhXq57S5TgePt/6iF5djb2cT69Dtg9xy kfAFaBEV0OaaWvfOQNU6mQWHfCzsV4nX5jJm+obShqv+UEYlLr1uqmk6ZRf6ezqX9h/F U90783EdWzMmp6RB2nKLT0rBYTuDmocSXNGzvaBzLpbgsoQzjkSxTlwOtRRfkP/iL3jZ e1BA== 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=3Yfs/8DKKdrc1dp/8fpKhalhfGjGCXihEUBk0IQ0evE=; b=I/EK1PlLqsVnTlGvCgi4XYLnqSsJPGB3GwsZfTSwR2Dt/xSOgtM8xFrFFwqLn0JjT8 QvZ7cqbnr99DpLzN7yvh+WZldQx0d11DQPiyr7AfBQ8V5V97vdt5xCbvTW9+LgYutX5c aiH4iPdjF9REo/9P7uSneQjSVZoirQttwCwfJFiVcjE9FZ+h7xl2SwcoGV6RGUZvfPCt vaFy3RIu6vM/cP5ZXvLlv9qgg6Dh6FqEjqGq22fzaj7iQhcgxkcFqjqZ3KpPv0Wxk6A2 0aPteQ7STlCT4ZMgZ37sHfZEY1jha0OAniM70iXsbj7vcgK/TPbLrb6PgRyKIJkJJ+7k /PwQ== X-Gm-Message-State: AMCzsaVD/LqFWDVN7knAiUrzro01qvV3Cwqi804PRdInCLnOroNK+bnA NItPHPZJNKAAskDdAd19mu4qOfxf X-Google-Smtp-Source: ABhQp+Sf8Lydz7qTkbbn0sYc4Q4QmZ0Ai9NicxevcMM9xRJS+CAKJNchh+zsl7lFdBmQ8TOK7Cfedg== X-Received: by 10.107.37.143 with SMTP id l137mr18420094iol.104.1508794958837; Mon, 23 Oct 2017 14:42:38 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:38 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 22/25] ext4 crypto: wire up FS_IOC_GET_ENCRYPTION_POLICY_EX Date: Mon, 23 Oct 2017 14:40:55 -0700 Message-Id: <20171023214058.128121-23-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers FS_IOC_GET_ENCRYPTION_POLICY_EX allows filesystem encryption users to retrieve the encryption policy for files and directories that use a v2 encryption policy. Unlike the original FS_IOC_GET_ENCRYPTION_POLICY, FS_IOC_GET_ENCRYPTION_POLICY_EX is also extensible to new versions of the policy struct that may be added in the future. Signed-off-by: Eric Biggers --- fs/ext4/ioctl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index b8a6765a556f..954042f311dc 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -978,6 +978,9 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case EXT4_IOC_GET_ENCRYPTION_POLICY: return fscrypt_ioctl_get_policy(filp, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg); + case FS_IOC_ADD_ENCRYPTION_KEY: if (!ext4_has_feature_encrypt(sb)) return -EOPNOTSUPP; @@ -1117,6 +1120,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC_SET_ENCRYPTION_POLICY: case EXT4_IOC_GET_ENCRYPTION_PWSALT: case EXT4_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_GET_ENCRYPTION_POLICY_EX: case FS_IOC_ADD_ENCRYPTION_KEY: case FS_IOC_REMOVE_ENCRYPTION_KEY: case FS_IOC_GET_ENCRYPTION_KEY_STATUS: From patchwork Mon Oct 23 21:40:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829657 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="upHOoXMz"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHx0D9kz9sPr for ; Tue, 24 Oct 2017 08:42:53 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932278AbdJWVmu (ORCPT ); Mon, 23 Oct 2017 17:42:50 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:45676 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932244AbdJWVml (ORCPT ); Mon, 23 Oct 2017 17:42:41 -0400 Received: by mail-io0-f193.google.com with SMTP id i38so21713958iod.2; Mon, 23 Oct 2017 14:42: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=q4ysmxM1Tc8eT1daOT1MgVemWjX6+/LfBa3mlqqW5UI=; b=upHOoXMzBopebaQdaFztRT7emgSQ3m9N/DGoH0rDp15I/217Vjv8l2lc2bIo0r5xM1 F917nKkqT4WktmiC64Z2T/gZC0CTA57BbNFAfrrmcSwFp1RfL01faY+vW7sfLDQrVsIr JVQjLKsCsPDFq9+DpN/9C4xW+6yery4+pW1vg5XxjaN5wYlEAwX3ARiAa42ypVMHFxGC X9SshHuMYLxFWa8eJx0uNfDUwXQoxJ/sRW/REbZFr94jKqTkIAavukU6QjcCEuoY8IaU MSTH5ey8FB+aWXDf/qcuUwGHt3Xw44ZP5vS4ZQjTpAKm71F4NO31XrnDi58Ja9Re9jtX 0uPQ== 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=q4ysmxM1Tc8eT1daOT1MgVemWjX6+/LfBa3mlqqW5UI=; b=EgRnRKceGrTLYC2rwUdVOVZNZLm8n1yQIo+zPJqEF8q/EAn5H9mcY5WglY7ww8/QLp HuuX77TFjso1vxlJgZS5WkNFUGfGSRaJ70qf967VAO+rwipkRmYWdUyopMW/ar8+3tyv Lrtzp+KQ1BVPQDIQjrFJzzeGS5r80uoGSxuJpEu1wzXycYSohuwcrE++BNH2zTMER3BH w9kwaQGMt9xjm9g36lXZw4jbgeD0me53UsJo/EXuI3WzKotjgUpXjBawLRE43b+WTvGY 1KxLyXcmwnbinOQbIPsnv23WGDKpeLnGcZUVaEJ+kw6DBKxXnTHGOaTozvrJb4utJIxj /lUQ== X-Gm-Message-State: AMCzsaXABQCgetbvxmWnhUnXsGwQnakPomwZWrVKfdP0Hc1Y17OI7g15 0/AWH74foAS/OUJVmtp7sqnOo0b1 X-Google-Smtp-Source: ABhQp+RuKIGNC4ir0VKFmsI9dyfMpyICcYq5Bu/hz1SRAzQQT2INFz4DqV4Iy9bJ/aCZbZa7+iJAnQ== X-Received: by 10.107.186.87 with SMTP id k84mr19309825iof.121.1508794960096; Mon, 23 Oct 2017 14:42:40 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:39 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 23/25] f2fs crypto: wire up FS_IOC_GET_ENCRYPTION_POLICY_EX Date: Mon, 23 Oct 2017 14:40:56 -0700 Message-Id: <20171023214058.128121-24-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers FS_IOC_GET_ENCRYPTION_POLICY_EX allows filesystem encryption users to retrieve the encryption policy for files and directories that use a v2 encryption policy. Unlike the original FS_IOC_GET_ENCRYPTION_POLICY, FS_IOC_GET_ENCRYPTION_POLICY_EX is also extensible to new versions of the policy struct that may be added in the future. Signed-off-by: Eric Biggers --- fs/f2fs/file.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0296a9594fe7..68b6ba732f25 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1887,11 +1887,6 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); } -static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) -{ - return fscrypt_ioctl_get_policy(filp, (void __user *)arg); -} - static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -2647,10 +2642,12 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_fitrim(filp, arg); case F2FS_IOC_SET_ENCRYPTION_POLICY: return f2fs_ioc_set_encryption_policy(filp, arg); - case F2FS_IOC_GET_ENCRYPTION_POLICY: - return f2fs_ioc_get_encryption_policy(filp, arg); case F2FS_IOC_GET_ENCRYPTION_PWSALT: return f2fs_ioc_get_encryption_pwsalt(filp, arg); + case F2FS_IOC_GET_ENCRYPTION_POLICY: + return fscrypt_ioctl_get_policy(filp, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg); case FS_IOC_ADD_ENCRYPTION_KEY: return fscrypt_ioctl_add_key(filp, (void __user *)arg); case FS_IOC_REMOVE_ENCRYPTION_KEY: @@ -2737,6 +2734,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_SET_ENCRYPTION_POLICY: case F2FS_IOC_GET_ENCRYPTION_PWSALT: case F2FS_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_GET_ENCRYPTION_POLICY_EX: case FS_IOC_ADD_ENCRYPTION_KEY: case FS_IOC_REMOVE_ENCRYPTION_KEY: case FS_IOC_GET_ENCRYPTION_KEY_STATUS: From patchwork Mon Oct 23 21:40:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829656 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="lufl455k"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVHt408qz9sPr for ; Tue, 24 Oct 2017 08:42:50 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932141AbdJWVmr (ORCPT ); Mon, 23 Oct 2017 17:42:47 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:51258 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932246AbdJWVmm (ORCPT ); Mon, 23 Oct 2017 17:42:42 -0400 Received: by mail-io0-f196.google.com with SMTP id b186so21740274iof.8; Mon, 23 Oct 2017 14:42:41 -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=JTvI73Oj3Dl4mvmZRRODzNdflOYA5Ch8R//atESWGDc=; b=lufl455kDTyzCtOLPyb4YHNopkfC/myBB5XPQwvenbfCRaK162lbyebysjlLAzfq0v wnrWP8FFOzt+t9dpjRVLqqtn9W1zCZY44cDZwkvNmh5R8mHXmPIyf9u8W6v9p1V0hmwQ eKyDE+ioUzeOEeHBengCVzvsNYsSScTfPcQvxM1lTNQCpgpDqcRjoUsHwV4Nb0GC4G/Q gVD890OziSY+W96hPwM6FxucoDf6aJUa1eoMmiQI+ZXsYsqXvRD4tGW8WjZIo6n2ZxTM cGy+qAjA7RNl44U0di4Z3yRtyfWvO1jkWfrd0wac5mcetmJcYaDOymW0v+CobyQGqMK3 q2fA== 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=JTvI73Oj3Dl4mvmZRRODzNdflOYA5Ch8R//atESWGDc=; b=AEd2XEQ0feMV/VapRruJmiAyVXQDvU4GhJgLUcRJHmrD/d3lvpi4B/Ma1RZ4mq/oQr 7tbdXWBJRFZS8Un1rKeFvh65T+msJdyGvqaH/kgZj6rJf5ZStIKH9q3uGnFPGQ8T6YYY bEUkBrGuqad9KYvxhTlt73GJvNLyx2xRUtdLo/igtwPnthyLT0WmL1IMDlUNomOgmfEn qwNfOZF/3IjR+M4zL86u01WT4kHZW0YpCZ50QLpOYxfqSR4Lt4q9ZgY+vVHCUYDNAkVR PmxrgyteXLFecMK6A41bA4x5ZCI/Xo/2yCBgqiF5TSurh5Dsrmnqdk3nXrTA8l0eJvfC 3VDA== X-Gm-Message-State: AMCzsaWg5g6cWFH/RKLLbpiIssoZJxbfNjwgJE14WjcYV0hfyG1Hh/GE 71Brkb8FG7/YoCLZYwNEqpMtFgTR X-Google-Smtp-Source: ABhQp+ScReiKSbXswNsep7vpcXMH2OsyA/pz5ActX2y6vakq4hkK11jTeQTIlDjGsbMneLjoLf4tRA== X-Received: by 10.107.146.134 with SMTP id u128mr18327542iod.209.1508794961361; Mon, 23 Oct 2017 14:42:41 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:40 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 24/25] ubifs crypto: wire up FS_IOC_GET_ENCRYPTION_POLICY_EX Date: Mon, 23 Oct 2017 14:40:57 -0700 Message-Id: <20171023214058.128121-25-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers FS_IOC_GET_ENCRYPTION_POLICY_EX allows filesystem encryption users to retrieve the encryption policy for files and directories that use a v2 encryption policy. Unlike the original FS_IOC_GET_ENCRYPTION_POLICY, FS_IOC_GET_ENCRYPTION_POLICY_EX is also extensible to new versions of the policy struct that may be added in the future. Signed-off-by: Eric Biggers --- fs/ubifs/ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 09ede2d1898f..2064c01ac864 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -197,13 +197,12 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EOPNOTSUPP; #endif } - case FS_IOC_GET_ENCRYPTION_POLICY: { -#ifdef CONFIG_UBIFS_FS_ENCRYPTION + + case FS_IOC_GET_ENCRYPTION_POLICY: return fscrypt_ioctl_get_policy(file, (void __user *)arg); -#else - return -EOPNOTSUPP; -#endif - } + + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg); case FS_IOC_ADD_ENCRYPTION_KEY: return fscrypt_ioctl_add_key(file, (void __user *)arg); @@ -231,6 +230,7 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case FS_IOC_SET_ENCRYPTION_POLICY: case FS_IOC_GET_ENCRYPTION_POLICY: + case FS_IOC_GET_ENCRYPTION_POLICY_EX: case FS_IOC_ADD_ENCRYPTION_KEY: case FS_IOC_REMOVE_ENCRYPTION_KEY: case FS_IOC_GET_ENCRYPTION_KEY_STATUS: From patchwork Mon Oct 23 21:40:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 829660 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hwUlc1Ba"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yLVJ76fB1z9sPr for ; Tue, 24 Oct 2017 08:43:03 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932281AbdJWVnB (ORCPT ); Mon, 23 Oct 2017 17:43:01 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:46201 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932258AbdJWVmo (ORCPT ); Mon, 23 Oct 2017 17:42:44 -0400 Received: by mail-io0-f193.google.com with SMTP id 101so21728034ioj.3; Mon, 23 Oct 2017 14:42:44 -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=W5YAIXr5iorMF90RSHZ03a665IYaPJu1c2ghmt3hDBM=; b=hwUlc1Ban8ukcpJES9TdmRodj2ZXWM4vhdbbCLVIX61zCL/L7zzT5DA3AD+C4j1RFX tN8UfWfnYeEXyEcgzaO5POeHX89TDTLectUUuRuNWyMEilUEosP3OUcPZWUeiccSJRrr dm+xWFpsUzkQX4HVZIYmXjy9ejHDhTaMZxt5Nbq0DciWTtrVQzUnJ2CLkkPUyJDwvLhD 1FB6xH8NVJFSB3R599+BcNHIbbrkxQ8j941+LT6R7cLbluqZnRxC0kUXXMKdGqwRS/k/ n6Cv9ZZe8Ovt9IAhkhsVnQ43gBHPjTZF+1uVCIIvXguIYdpiQBy4to1QBJm+v5gruFR+ 509w== 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=W5YAIXr5iorMF90RSHZ03a665IYaPJu1c2ghmt3hDBM=; b=CARv7iLFCye0H1c2kEUoBjmD5mGUN+G1jLValmJUXC2tcUmVFAoACp0RrNIux1lB98 EbCvqJWOuq4nXC28Wzvdv04OQhjT0UHgxhLHpfNUiHjzbK1eTWjvxyQHqyg5+pswx0ee DGpmW4Bpa61wwBwKEgRgmC0ECJtFlbNIYHDtO0igagaqwq8OKZ8RBxu5g92xY7YK9fye U7yEEJFYHMpBB4r4YQeMsm8cimleW7lyTgBCfKwATl2dOtjMvBrxmD+7fSmHOZNgaM0S qgZv52E4HQkLxCZd4ywRQZu+qCr1/GsvdnCW08L29B+GbR/EpXQ+dun0lboHmTr9lu4r zEgw== X-Gm-Message-State: AMCzsaXQQkkB3sQ4rojDEIVh8eZZ0/mAmydoa1e7reLcMi7vNAa3j4yh mqR8Gi5C52GZVtQL3w/pqiNQIA62 X-Google-Smtp-Source: ABhQp+SbRWGjRLrDRH+e2Dtw91aiuz2YnYahLxaMLCaD+ge9ZjC/A4+Q1RR3tIRS8rFXf5okVtGatA== X-Received: by 10.107.8.100 with SMTP id 97mr18830179ioi.83.1508794962915; Mon, 23 Oct 2017 14:42:42 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:42 -0700 (PDT) From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers Subject: [RFC PATCH 25/25] fscrypt: document the new ioctls and policy version Date: Mon, 23 Oct 2017 14:40:58 -0700 Message-Id: <20171023214058.128121-26-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Eric Biggers Update the fscrypt documentation file to catch up to all the latest changes, including the new ioctls to manage master encryption keys in the filesystem-level keyring, the support for v2 encryption policies, and the new key derivation function. Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 565 ++++++++++++++++++++++++++++------ 1 file changed, 472 insertions(+), 93 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index f12956f3f1f8..f2d0ff2269f3 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -72,6 +72,9 @@ Online attacks fscrypt (and storage encryption in general) can only provide limited protection, if any at all, against online attacks. In detail: +Side-channel attacks +~~~~~~~~~~~~~~~~~~~~ + fscrypt is only resistant to side-channel attacks, such as timing or electromagnetic attacks, to the extent that the underlying Linux Cryptographic API algorithms are. If a vulnerable algorithm is used, @@ -80,29 +83,86 @@ attacker to mount a side channel attack against the online system. Side channel attacks may also be mounted against applications consuming decrypted data. -After an encryption key has been provided, fscrypt is not designed to -hide the plaintext file contents or filenames from other users on the -same system, regardless of the visibility of the keyring key. -Instead, existing access control mechanisms such as file mode bits, -POSIX ACLs, LSMs, or mount namespaces should be used for this purpose. -Also note that as long as the encryption keys are *anywhere* in -memory, an online attacker can necessarily compromise them by mounting -a physical attack or by exploiting any kernel security vulnerability -which provides an arbitrary memory read primitive. - -While it is ostensibly possible to "evict" keys from the system, -recently accessed encrypted files will remain accessible at least -until the filesystem is unmounted or the VFS caches are dropped, e.g. -using ``echo 2 > /proc/sys/vm/drop_caches``. Even after that, if the -RAM is compromised before being powered off, it will likely still be -possible to recover portions of the plaintext file contents, if not -some of the encryption keys as well. (Since Linux v4.12, all -in-kernel keys related to fscrypt are sanitized before being freed. -However, userspace would need to do its part as well.) - -Currently, fscrypt does not prevent a user from maliciously providing -an incorrect key for another user's existing encrypted files. A -protection against this is planned. +Unauthorized file access +~~~~~~~~~~~~~~~~~~~~~~~~ + +After an encryption key has been added, fscrypt does not hide the +plaintext file contents or filenames from other users on the same +system. Instead, existing access control mechanisms such as file mode +bits, POSIX ACLs, LSMs, or namespaces should be used for this purpose. + +(For the reasoning behind this, understand that while the key is +added, the confidentiality of the data, from the perspective of the +system itself, is *not* protected by the mathematical properties of +encryption but rather only by the correctness of the kernel. +Therefore, any encryption-specific access control checks would merely +be enforced by kernel *code* and therefore would be largely redundant +with the wide variety of access control mechanisms already available.) + +Kernel compromise +~~~~~~~~~~~~~~~~~ + +An attacker who compromises the system enough to read from arbitrary +memory, e.g. by mounting a physical attack or by exploiting a kernel +security vulnerability, can compromise all encryption keys that are +currently in use. + +However, fscrypt does allow an encryption key to be removed from the +kernel, which may protect it from later compromise. + +In more detail, the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl will wipe a +master encryption key from kernel memory. Moreover, it will try to +evict all cached inodes which had been "unlocked" using the key, +thereby wiping their derived encryption keys and making them once +again appear "locked", i.e. in ciphertext or encrypted form. + +However, FS_IOC_REMOVE_ENCRYPTION_KEY has some limitations: + +- Derived keys for in-use files will *not* be removed or wiped. + Therefore, for maximum effect, userspace should close the relevant + encrypted files and directories before removing a master key, as + well as kill any processes whose working directory is in an affected + encrypted directory. + +- The kernel cannot magically wipe copies of the master key(s) that + userspace might have as well. Therefore, userspace must wipe all + copies of the master key(s) it makes as well. Naturally, the same + also applies to all higher levels in the key hierarchy, e.g. to all + key(s) that are used to wrap or derive the fscrypt master keys. + Userspace should also follow other security precautions such as + mlock()ing memory containing keys to prevent it from being swapped + out. + +- In general, decrypted contents and filenames in the kernel VFS + caches are freed but not wiped. Therefore, portions thereof may be + recoverable from freed memory, even after the corresponding key(s) + were wiped. To partially solve this, you may enable page poisoning + by enabling CONFIG_PAGE_POISONING in your kernel config and adding + page_poison=1 to your kernel command line. However, that has a + performance cost. + +- Secret keys might still exist in CPU registers, in crypto + accelerator hardware (if used by the crypto API to provide any of + the algorithms), or in other places not explicitly considered here. + +Limitations of v1 policies +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The original encryption policy version (which we call "v1") had some +weaknesses with respect to online attacks: + +- There was no verification that the provided master key was correct. + Therefore, malicious users could associate the wrong key with + encrypted files, even files to which they had only read-only access. + +- A compromise of any file's derived encryption key also compromised + the master key it was derived from. + +- Non-root users could not securely remove encryption keys. + +All the above problems are fixed with v2 encryption policies +(:c:type:`fscrypt_policy_v2`). For this reason, it's recommended to +use v2 encryption policies for all new encrypted directories. Key hierarchy ============= @@ -167,19 +227,27 @@ master keys or to support rotating master keys. Instead, the master keys may be wrapped in userspace, e.g. as done by the `fscrypt `_ tool. -The current KDF encrypts the master key using the 16-byte nonce as an -AES-128-ECB key. The output is used as the derived key. If the -output is longer than needed, then it is truncated to the needed -length. Truncation is the norm for directories and symlinks, since -those use the CTS-CBC encryption mode which requires a key half as -long as that required by the XTS encryption mode. - -Note: this KDF meets the primary security requirement, which is to -produce unique derived keys that preserve the entropy of the master -key, assuming that the master key is already a good pseudorandom key. -However, it is nonstandard and has some problems such as being -reversible, so it is generally considered to be a mistake! It may be -replaced with HKDF or another more standard KDF in the future. +A different KDF is used depending on the encryption policy version: + +For v1 encryption policies, the KDF is somewhat ad-hoc: we encrypt the +master key with AES-128-ECB using the file's 16-byte nonce as the AES +key, and the resulting ciphertext is used as the derived key. If the +master key is longer than the derived key, then only the needed prefix +of the ciphertext is used. Truncation is the norm for directories and +symlinks, since those use the CTS-CBC encryption mode which requires a +key half as long as that required by the XTS encryption mode. + +For v2 encryption policies, the KDF is HKDF-SHA512. HKDF is preferred +to the AES-based KDF because HKDF is standardized and has a number of +desirable properties such as being nonreversible and evenly +distributing the entropy from the master key. To derive a file's +encryption key using HKDF, the master key is used as the "input key +material", a fixed value is used as the "salt", and the file's 16-byte +nonce prefixed with a context byte is used as the +"application-specific information string". (A fixed salt is used +because there is no random salt available on a per-master-key basis, +and the master keys should already be good pseudorandom keys that are +long enough to make dictionary attacks infeasible.) Encryption modes and usage ========================== @@ -249,21 +317,38 @@ Setting an encryption policy The FS_IOC_SET_ENCRYPTION_POLICY ioctl sets an encryption policy on an empty directory or verifies that a directory or regular file already has the specified encryption policy. It takes in a pointer to a -:c:type:`struct fscrypt_policy`, defined as follows:: - - #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 +:c:type:`struct fscrypt_policy_v1` or a :c:type:`struct +fscrypt_policy_v2`, defined as follows:: - struct fscrypt_policy { + #define FSCRYPT_POLICY_VERSION_LEGACY 0 + #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 + struct fscrypt_policy_v1 { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; + #define fscrypt_policy fscrypt_policy_v1 + + #define FSCRYPT_POLICY_VERSION_2 2 + #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 + struct fscrypt_policy_v2 { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 reserved[4]; + __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + }; This structure must be initialized as follows: -- ``version`` must be 0. +- ``version`` must be FSCRYPT_POLICY_VERSION_LEGACY (0) if the struct + is :c:type:`fscrypt_policy_v1` or FSCRYPT_POLICY_VERSION_2 (2) if + the struct is :c:type:`fscrypt_policy_v2`. (Note: we refer to the + legacy policy version as "v1", though its version number was really + 0.) For new encrypted directories, use v2 policies. - ``contents_encryption_mode`` and ``filenames_encryption_mode`` must be set to constants from ```` which identify the @@ -276,15 +361,25 @@ This structure must be initialized as follows: identifies the amount of NUL-padding to use when encrypting filenames. If unsure, use FSCRYPT_POLICY_FLAGS_PAD_32 (0x3). -- ``master_key_descriptor`` specifies how to find the master key in - the keyring; see `Adding keys`_. It is up to userspace to choose a - unique ``master_key_descriptor`` for each master key. The e4crypt - and fscrypt tools use the first 8 bytes of +- For v2 encryption policies, ``reserved`` must be zeroed. + +- For v1 encryption policies, ``master_key_descriptor`` specifies how + to find the master key in a keyring; see `Adding keys`_. It is up + to userspace to choose a unique ``master_key_descriptor`` for each + master key. The e4crypt and fscrypt tools use the first 8 bytes of ``SHA-512(SHA-512(master_key))``, but this particular scheme is not required. Also, the master key need not be in the keyring yet when FS_IOC_SET_ENCRYPTION_POLICY is executed. However, it must be added before any files can be created in the encrypted directory. + For v2 encryption policies, ``master_key_descriptor`` has been + replaced with ``master_key_identifier``, which is longer and cannot + be arbitrarily chosen. Instead, the key must first be added using + FS_IOC_ADD_ENCRYPTION_KEY, as described in `Adding keys`_. Then, + the ``key_spec.identifier`` the kernel returned in the + :c:type:`struct fscrypt_add_key_args` must be used as the + ``master_key_identifier`` in the ``struct fscrypt_policy_v2``. + If the file is not yet encrypted, then FS_IOC_SET_ENCRYPTION_POLICY verifies that the file is an empty directory. If so, the specified encryption policy is assigned to the directory, turning it into an @@ -300,6 +395,15 @@ policy exactly matches the actual one. If they match, then the ioctl returns 0. Otherwise, it fails with EEXIST. This works on both regular files and directories, including nonempty directories. +When a v2 encryption policy is assigned to a directory, it is also +required that either the specified key has been added by the current +user, or the caller has CAP_FOWNER in the initial user namespace. +(This is needed to prevent a user from encrypting their data with +another user's key.) The key must remain added while +FS_IOC_SET_ENCRYPTION_POLICY is executing. However, if the new +encrypted directory does not need to be accessed immediately, then the +key can be removed right away afterwards. + Note that the ext4 filesystem does not allow the root directory to be encrypted, even if it is empty. Users who want to encrypt an entire filesystem with one key should consider using dm-crypt instead. @@ -312,7 +416,9 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: - ``EEXIST``: the file is already encrypted with an encryption policy different from the one specified - ``EINVAL``: an invalid encryption policy was specified (invalid - version, mode(s), or flags) + version, mode(s), or flags; or reserved bits were set) +- ``ENOKEY``: a v2 encryption policy was specified, but the key with + the specified ``master_key_identifier`` has not been added - ``ENOTDIR``: the file is unencrypted and is a regular file, not a directory - ``ENOTEMPTY``: the file is unencrypted and is a nonempty directory @@ -331,25 +437,82 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: Getting an encryption policy ---------------------------- -The FS_IOC_GET_ENCRYPTION_POLICY ioctl retrieves the :c:type:`struct -fscrypt_policy`, if any, for a directory or regular file. See above -for the struct definition. No additional permissions are required -beyond the ability to open the file. +Two ioctls are available to get a file's encryption policy: + +- FS_IOC_GET_ENCRYPTION_POLICY_EX +- FS_IOC_GET_ENCRYPTION_POLICY + +The extended (_EX) version of the ioctl is more general and is +recommended to use when possible. However, on older kernels only the +original ioctl is available. Applications should try the extended +version, and if it fails with ENOTTY fall back to the original +version. + +Preferred method +~~~~~~~~~~~~~~~~ + +The FS_IOC_GET_ENCRYPTION_POLICY_EX ioctl retrieves the encryption +policy, if any, for a directory or regular file. No additional +permissions are required beyond the ability to open the file. It +takes in a pointer to a buffer formatted as a :c:type:`struct +fscrypt_get_policy_ex_args`, defined as follows:: + + struct fscrypt_get_policy_ex_args { + __u64 size; + union { + __u8 version; + struct fscrypt_policy_v1 v1; + struct fscrypt_policy_v2 v2; + } policy; + }; + +The caller must initialize ``size`` to the size of the buffer in +bytes, including both the ``size`` field and the space available for +the policy struct. It is recommended to use ``sizeof(struct +fscrypt_get_policy_ex_args)``. + +On successful return, ``size`` is set to the actual number of bytes +returned, including both the ``size`` field and the actual size of the +returned policy struct. In addition, the ``version`` field should be +used to determine the actual policy version returned. Note that the +version code for the "v1" policy is actually 0 +(FSCRYPT_POLICY_VERSION_LEGACY). -FS_IOC_GET_ENCRYPTION_POLICY can fail with the following errors: +FS_IOC_GET_ENCRYPTION_POLICY_EX can fail with the following errors: - ``EINVAL``: the file is encrypted, but it uses an unrecognized - encryption context format + encryption policy version; or, an invalid ``size`` was provided - ``ENODATA``: the file is not encrypted -- ``ENOTTY``: this type of filesystem does not implement encryption +- ``ENOTTY``: this type of filesystem does not implement encryption, + or this kernel is too old to support FS_IOC_GET_ENCRYPTION_POLICY_EX + (try FS_IOC_GET_ENCRYPTION_POLICY instead) - ``EOPNOTSUPP``: the kernel was not configured with encryption support for this filesystem +- ``EOVERFLOW``: the file is encrypted and uses a recognized + encryption policy version, but the policy struct does not fit into + the provided buffer Note: if you only need to know whether a file is encrypted or not, on most filesystems it is also possible to use the FS_IOC_GETFLAGS ioctl and check for FS_ENCRYPT_FL, or to use the statx() system call and check for STATX_ATTR_ENCRYPTED in stx_attributes. +Legacy method +~~~~~~~~~~~~~ + +The FS_IOC_GET_ENCRYPTION_POLICY ioctl can also retrieve the +encryption policy, if any, for a directory or regular file. However, +unlike the extended version (FS_IOC_GET_ENCRYPTION_POLICY_EX), +FS_IOC_GET_ENCRYPTION_POLICY only supports the original policy +version. It takes in a pointer directly to a :c:type:`struct +fscrypt_policy_v1` rather than a :c:type:`struct +fscrypt_get_policy_ex_args`. + +The error codes for FS_IOC_GET_ENCRYPTION_POLICY are the same as those +for FS_IOC_GET_ENCRYPTION_POLICY_EX, except that +FS_IOC_GET_ENCRYPTION_POLICY also returns ``EINVAL`` if the file is +encrypted using a newer encryption policy version. + Getting the per-filesystem salt ------------------------------- @@ -365,8 +528,97 @@ generate and manage any needed salt(s) in userspace. Adding keys ----------- -To provide a master key, userspace must add it to an appropriate -keyring using the add_key() system call (see: +Preferred method +~~~~~~~~~~~~~~~~ + +The FS_IOC_ADD_ENCRYPTION_KEY ioctl adds a master encryption key to +the filesystem, making all files on the filesystem which were +encrypted using that key appear "unlocked", i.e. in plaintext form. +It takes in a pointer to a :c:type:`struct fscrypt_add_key_args`, +defined as follows:: + + struct fscrypt_add_key_args { + __u32 raw_size; + __u32 reserved1; + __u64 reserved2[2]; + struct fscrypt_key_specifier key_spec; + __u8 raw[]; + }; + + struct fscrypt_key_specifier { + __u32 type; + #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 + #define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 + __u32 reserved; + union { + __u8 max_specifier[32]; + __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; + __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + }; + }; + +:c:type:`struct fscrypt_add_key_args` must be initialized as follows: + +- ``raw_size`` must be the size of the ``raw`` key provided, in bytes. + +- If the key is being added for use by v1 encryption policies, then + ``key_spec.type`` must contain FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR, and + ``key_spec.descriptor`` must contain the descriptor of the key being + added, corresponding to the value in the ``master_key_descriptor`` + field of :c:type:`struct fscrypt_policy_v1`. To add this type of + key, the calling process must have the CAP_SYS_ADMIN capability in + the initial user namespace. + + Alternatively, if the key is being added for use by v2 encryption + policies, then ``key_spec.type`` must contain + FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, and ``key_spec.identifier`` is an + *output* field which the kernel fills in with a cryptographic hash + of the key. To add this type of key, the calling process does not + need any privileges. However, the number of keys that can be added + is limited by the user's quota for the keyrings service (see + ``Documentation/security/keys/core.rst``). + +- ``raw`` is a variable-length field which must contain the actual + key, ``raw_size`` bytes long. + +- All reserved fields must be zeroed. + +FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors: + +- ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the + caller does not have the CAP_SYS_ADMIN capability in the initial + user namespace +- ``EDQUOT``: the key quota for this user would be exceeded by adding + the key +- ``EINVAL``: invalid key size or key specifier type, or reserved bits + were set +- ``ENOTTY``: this type of filesystem does not implement encryption +- ``EOPNOTSUPP``: the kernel was not configured with encryption + support for this filesystem, or the filesystem superblock has not + had encryption enabled on it + +Legacy method +~~~~~~~~~~~~~ + +For v1 encryption policies, a master encryption key can also be +provided by adding it to a process-subscribed keyring, e.g. to a +session keyring, or to a user keyring if the user keyring is linked +into the session keyring. + +This method is deprecated (and not supported for v2 encryption +policies) for several reasons. First, it cannot be used in +combination with FS_IOC_REMOVE_ENCRYPTION_KEY (see `Removing keys`_), +so for removing a key a workaround such as keyctl_unlink() in +combination with ``sync; echo 2 > /proc/sys/vm/drop_caches`` would +have to be used. Second, it doesn't match the fact that the +locked/unlocked status of encrypted files (i.e. whether they appear to +be in plaintext form or in ciphertext form) is global. This mismatch +has caused much confusion as well as real problems when processes +running under different UIDs, such as a ``sudo`` command, need to +access encrypted files. + +Nevertheless, to add a key to one of the process-subscribed keyrings, +the add_key() system call can be used (see: ``Documentation/security/keys/core.rst``). The key type must be "logon"; keys of this type are kept in kernel memory and cannot be read back by userspace. The key description must be "fscrypt:" @@ -391,26 +643,143 @@ with a filesystem-specific prefix such as "ext4:". However, the filesystem-specific prefixes are deprecated and should not be used in new programs. -There are several different types of keyrings in which encryption keys -may be placed, such as a session keyring, a user session keyring, or a -user keyring. Each key must be placed in a keyring that is "attached" -to all processes that might need to access files encrypted with it, in -the sense that request_key() will find the key. Generally, if only -processes belonging to a specific user need to access a given -encrypted directory and no session keyring has been installed, then -that directory's key should be placed in that user's user session -keyring or user keyring. Otherwise, a session keyring should be -installed if needed, and the key should be linked into that session -keyring, or in a keyring linked into that session keyring. - -Note: introducing the complex visibility semantics of keyrings here -was arguably a mistake --- especially given that by design, after any -process successfully opens an encrypted file (thereby setting up the -per-file key), possessing the keyring key is not actually required for -any process to read/write the file until its in-memory inode is -evicted. In the future there probably should be a way to provide keys -directly to the filesystem instead, which would make the intended -semantics clearer. +Removing keys +------------- + +The FS_IOC_REMOVE_ENCRYPTION_KEY ioctl can be used to remove a master +encryption key from the kernel, wiping the corresponding secrets from +memory and causing any files which were "unlocked" with the key to +appear "locked" again. It takes in a pointer to a :c:type:`struct +fscrypt_remove_key_args`, defined as follows:: + + struct fscrypt_remove_key_args { + __u32 flags; + #define FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS 0x00000001 + __u32 reserved1; + __u64 reserved2[2]; + struct fscrypt_key_specifier key_spec; + }; + +This structure must be initialized as follows: + +- ``flags`` can contain the following flags: + + - ``FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS`` specifies that the key + should be removed even if it has also been added by other users. + Specifying this flag requires the CAP_SYS_ADMIN capability in + the initial user namespace. + +- The key to remove is specified by ``key_spec``: + + - To remove a key used by v1 encryption policies, set + ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR and fill + in ``key_spec.descriptor``. To remove this type of key, the + calling process must have the CAP_SYS_ADMIN capability in the + initial user namespace. + + - To remove a key used by v2 encryption policies, set + ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER and fill + in ``key_spec.identifier``. To remove this type of key, no + privileges are needed. However, users can only remove keys that + they added themselves, subject to privileged override with + FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS. + +- All reserved fields must be zeroed. + +FS_IOC_REMOVE_ENCRYPTION_KEY can fail with the following errors: + +- ``EACCES``: The FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR key specifier type + and/or the FSCRYPT_REMOVE_KEY_FLAG_ALL_USERS flag was specified, but + the caller does not have the CAP_SYS_ADMIN capability in the initial + user namespace +- ``EBUSY``: the master key secret was wiped from memory, but some + files which were unlocked with it are still in use. Such files + could not be locked, nor could their per-file keys be wiped from + memory. The ioctl may be retried later to re-attempt locking the + remaining files. +- ``EINVAL``: invalid flags or key specifier type, or reserved bits + were set +- ``ENOKEY``: the key is not present or has already been removed +- ``ENOTTY``: this type of filesystem does not implement encryption +- ``EOPNOTSUPP``: the kernel was not configured with encryption + support for this filesystem, or the filesystem superblock has not + had encryption enabled on it +- ``EUSERS``: the key cannot be removed because other users have added + it too + +Before using this ioctl, please read the `Kernel compromise`_ section +for a discussion of the security goals and limitations of this ioctl. + +Getting key status +------------------ + +The FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl retrieves the status of a +master encryption key. It takes in a pointer to a :c:type:`struct +fscrypt_get_key_status_args`, defined as follows:: + + struct fscrypt_get_key_status_args { + /* input */ + __u64 reserved1[3]; + struct fscrypt_key_specifier key_spec; + + /* output */ + __u32 status; + #define FSCRYPT_KEY_STATUS_ABSENT 1 + #define FSCRYPT_KEY_STATUS_PRESENT 2 + #define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 + __u32 status_flags; + #define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 + __u32 user_count; + __u32 reserved2; + __u64 reserved3[6]; + }; + +The caller must zero ``reserved1``, then fill in ``key_spec``: + + - To get the status of a key for v1 encryption policies, set + ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR and fill + in ``key_spec.descriptor``. + + - To get the status of a key for v2 encryption policies, set + ``key_spec.type`` to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER and fill + in ``key_spec.identifier``. + +On success, 0 is returned and the kernel fills in the output fields: + +- ``status`` indicates whether the key is absent, present, or + incompletely removed. Incompletely removed means that the master + secret has been removed, but some files are still in use; i.e., + FS_IOC_REMOVE_ENCRYPTION_KEY returned EBUSY. + +- ``status_flags`` can contain the following flags: + + - ``FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF`` indicates that the key + has added by the current user. This is only set for keys + identified by ``identifier`` rather than by ``descriptor``. + +- ``user_count`` specifies the number of users who have added the key. + This is only set for keys identified by ``identifier`` rather than + by ``descriptor``. + +FS_IOC_GET_ENCRYPTION_KEY_STATUS can fail with the following errors: + +- ``EINVAL``: invalid key specifier type, or reserved bits were set +- ``ENOTTY``: this type of filesystem does not implement encryption +- ``EOPNOTSUPP``: the kernel was not configured with encryption + support for this filesystem, or the filesystem superblock has not + had encryption enabled on it + +Among other use cases, FS_IOC_GET_ENCRYPTION_KEY_STATUS might be +useful for determining whether the key for a given encrypted directory +needs to be added before prompting the user for the passphrase needed +to derive the key. + +FS_IOC_GET_ENCRYPTION_KEY_STATUS can only get the status of keys in +the filesystem-level keyring, i.e. the keyring managed by +FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY. It cannot +get the status of a key that has only been added for use by v1 +encryption policies using the legacy mechanism involving +process-subscribed keyrings. Access semantics ================ @@ -459,7 +828,7 @@ Without the key Some filesystem operations may be performed on encrypted regular files, directories, and symlinks even before their encryption key has -been provided: +been added, or after their encryption key has been removed: - File metadata may be read, e.g. using stat(). @@ -524,20 +893,20 @@ Encryption context ------------------ An encryption policy is represented on-disk by a :c:type:`struct -fscrypt_context`. It is up to individual filesystems to decide where -to store it, but normally it would be stored in a hidden extended -attribute. It should *not* be exposed by the xattr-related system -calls such as getxattr() and setxattr() because of the special -semantics of the encryption xattr. (In particular, there would be -much confusion if an encryption policy were to be added to or removed -from anything other than an empty directory.) The struct is defined -as follows:: +fscrypt_context_v1` or a :c:type:`struct fscrypt_context_v2`. It is +up to individual filesystems to decide where to store it, but normally +it would be stored in a hidden extended attribute. It should *not* be +exposed by the xattr-related system calls such as getxattr() and +setxattr() because of the special semantics of the encryption xattr. +(In particular, there would be much confusion if an encryption policy +were to be added to or removed from anything other than an empty +directory.) These structs are defined as follows:: - #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 #define FS_KEY_DERIVATION_NONCE_SIZE 16 - struct fscrypt_context { - u8 format; + #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 + struct fscrypt_context_v1 { + u8 version; u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; @@ -545,12 +914,22 @@ as follows:: u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; }; -Note that :c:type:`struct fscrypt_context` contains the same -information as :c:type:`struct fscrypt_policy` (see `Setting an -encryption policy`_), except that :c:type:`struct fscrypt_context` -also contains a nonce. The nonce is randomly generated by the kernel -and is used to derive the inode's encryption key as described in -`Per-file keys`_. + #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 + struct fscrypt_context_v2 { + u8 version; + u8 contents_encryption_mode; + u8 filenames_encryption_mode; + u8 flags; + u8 reserved[4]; + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; + }; + +Note that the context structs contain the same information as the +corresponding policy structs (see `Setting an encryption policy`_), +except that the context structs also contain a nonce. The nonce is +randomly generated by the kernel and is used to derive the inode's +encryption key as described in `Per-file keys`_. Data path changes -----------------