From patchwork Sun Apr 3 05:21:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 8733061 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 3EE859F71A for ; Sun, 3 Apr 2016 05:27:16 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EAFC02022D for ; Sun, 3 Apr 2016 05:27:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8416120254 for ; Sun, 3 Apr 2016 05:27:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752877AbcDCF0u (ORCPT ); Sun, 3 Apr 2016 01:26:50 -0400 Received: from mail-ig0-f195.google.com ([209.85.213.195]:34536 "EHLO mail-ig0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752368AbcDCFXl (ORCPT ); Sun, 3 Apr 2016 01:23:41 -0400 Received: by mail-ig0-f195.google.com with SMTP id mh10so7638239igb.1; Sat, 02 Apr 2016 22:23:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=0MlYVZO76ZK+2b7i4hDS9b59x0fAsRBkbfV4DkQozLA=; b=jrqmQTs1XODQsYZhrQlEt+Zb5KL4LEKrp0mndcqqcM2sJ09Bfz0yc9UozVucGXgFyt 526pP+FsWdW7IV5eWCIbHDWd6Z2ZqnjkHcTTYMbpJQa9e09UMWXIaCOtXXBf3K/ZsfbG aLYDeokC1CKi2Jylu+vDba7ofEBGwGvKVJKkb39dG38F4vzfkoAITAoHQzh8KQ9sUlBV fsBMR98tOoDbzG9sPWLkvV8/V0RJvKeGTIHkxq0KgIxg9Mouz3a8+NZL0dZL3b3RRjmm iPjJS5XO749LtAl5oDNNdu50Hg5Lv1kjL6ck6pZBU08SItndFYUXTCA+9pl5yKogOPo1 NWfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=0MlYVZO76ZK+2b7i4hDS9b59x0fAsRBkbfV4DkQozLA=; b=EZlM3rKaHCTISHqCGFglrlVc5kdS9dyF1a826Qn15NRinrbQ/ffxSVwf3xfNlEPx1h HUGL3Efd9ZB+zYSluKFUtx+4pNgkIZufwbkestdn5qivqhRYlr/gCFmjY8T/GsctC+In 9j+elHDgvcGKL350hiBtKGIuGdBjwwWSQwx5t19mubHCgV66DUpSqwfh1LVpXlnZb0kH BQOgWmskF11h02fVnG90kU6Lp7NFlbugKhF9WhYIC7dptOIw1qOc5ypEOYkIAy/d6t/4 S2uwklUn8pHqUmha76AM8ushG3bxVQe4JdoVkYMCEcolHFW2shv1ZFVlMIK4/TM2ZgMg W6iw== X-Gm-Message-State: AD7BkJLyEfd0fIuw7YBkPFC6/GYXmcdnw57wocy0ukOKhNIwQs/RwpRi3kTtj5fD0nmEjQ== X-Received: by 10.50.59.242 with SMTP id c18mr5782651igr.4.1459661020769; Sat, 02 Apr 2016 22:23:40 -0700 (PDT) Received: from localhost.localdomain (c-24-7-245-123.hsd1.mn.comcast.net. [24.7.245.123]) by smtp.gmail.com with ESMTPSA id je6sm2914954igb.15.2016.04.02.22.23.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 02 Apr 2016 22:23:40 -0700 (PDT) From: Eric Biggers To: linux-fsdevel@vger.kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, jaegeuk@kernel.org, tytso@mit.edu, mhalcrow@google.com, Eric Biggers Subject: [PATCH 05/13] fscrypto: comment improvements and fixes Date: Sun, 3 Apr 2016 00:21:56 -0500 Message-Id: <1459660924-2960-6-git-send-email-ebiggers3@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1459660924-2960-1-git-send-email-ebiggers3@gmail.com> References: <1459660924-2960-1-git-send-email-ebiggers3@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 13 ++++++------ fs/crypto/fname.c | 33 ++++++++++++++++++++++++++---- fs/crypto/keyinfo.c | 8 ++++++++ include/linux/fscrypto.h | 53 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 6e550ec..836c0f2 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -83,8 +83,8 @@ EXPORT_SYMBOL(fscrypt_release_ctx); * * Allocates and initializes an encryption context. * - * Return: An allocated and initialized encryption context on success; error - * value or NULL otherwise. + * Return: An allocated and initialized encryption context on success; an error + * value otherwise. */ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode) { @@ -222,7 +222,7 @@ static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx) * release the bounce buffer and the encryption context. * * Return: An allocated page with the encrypted content on success. Else, an - * error value or NULL. + * error value. */ struct page *fscrypt_encrypt_page(struct inode *inode, struct page *plaintext_page) @@ -261,10 +261,10 @@ errout: EXPORT_SYMBOL(fscrypt_encrypt_page); /** - * f2crypt_decrypt_page() - Decrypts a page in-place + * fscrypt_decrypt_page() - Decrypts a page in-place * @page: The page to decrypt. Must be locked. * - * Decrypts page in-place using the ctx encryption context. + * Decrypts a page in-place using the host inode's encryption context. * * Called from the read completion callback. * @@ -358,7 +358,6 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) (1 << KEY_FLAG_DEAD)))) ci = NULL; - /* this should eventually be an flag in d_flags */ spin_lock(&dentry->d_lock); cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY; spin_unlock(&dentry->d_lock); @@ -368,7 +367,7 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) * If the dentry was cached without the key, and it is a * negative dentry, it might be a valid name. We can't check * if the key has since been made available due to locking - * reasons, so we fail the validation so ext4_lookup() can do + * reasons, so we fail the validation so ->lookup() can do * this check. * * We also fail the validation if the dentry was created with diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index ceaa815..cd0eae8 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -178,8 +178,11 @@ static const char *lookup_table = /** * digest_encode() - * - * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. + * Encodes the input digest using characters from the set [A-Za-z0-9+,]. * The encoded string is roughly 4/3 times the size of the input string. + * + * Note that the result of this encoding is for presentation purposes only; it + * is not persisted in the filesystem. */ static int digest_encode(const char *src, int len, char *dst) { @@ -239,7 +242,7 @@ u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen) EXPORT_SYMBOL(fscrypt_fname_encrypted_size); /** - * fscrypt_fname_crypto_alloc_obuff() - + * fscrypt_fname_alloc_buffer() - * * Allocates an output buffer that is sufficient for the crypto operation * specified by the context and the direction. @@ -264,7 +267,7 @@ int fscrypt_fname_alloc_buffer(struct inode *inode, EXPORT_SYMBOL(fscrypt_fname_alloc_buffer); /** - * fscrypt_fname_crypto_free_buffer() - + * fscrypt_fname_free_buffer() - * * Frees the buffer allocated for crypto operation. */ @@ -280,6 +283,12 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer); /** * fscrypt_fname_disk_to_usr() - converts a filename from disk space to user * space + * + * The caller must have used fscrypt_fname_alloc_buffer() to allocate sufficient + * memory for the @oname string. Also, fscrypt_load_encryption_info() must have + * been already called on the inode. + * + * Return: the length of the user space name or a negative errno value. */ int fscrypt_fname_disk_to_usr(struct inode *inode, u32 hash, u32 minor_hash, @@ -290,6 +299,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, char buf[24]; int ret; + /* Leave . and .. alone. */ if (fscrypt_is_dot_dotdot(&qname)) { oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; @@ -300,14 +310,19 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, if (iname->len < FS_CRYPTO_BLOCK_SIZE) return -EUCLEAN; + /* Decrypt the name if we have the key. */ if (inode->i_crypt_info) return fname_decrypt(inode, iname, oname); if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) { + /* Short name: encode the encrypted name. */ ret = digest_encode(iname->name, iname->len, oname->name); oname->len = ret; return ret; } + + /* Long name: encode the name hash (if a directory entry, not a symlink) + * concatenated with the last 16 bytes of the encrypted name. */ if (hash) { memcpy(buf, &hash, 4); memcpy(buf + 4, &minor_hash, 4); @@ -325,23 +340,33 @@ EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); /** * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk * space + * + * The caller must have used fscrypt_fname_alloc_buffer() to allocate sufficient + * memory for the @oname string. Also, fscrypt_load_encryption_info() must have + * been already called on the inode. + * + * Return: the length of the disk space name or a negative errno value. */ int fscrypt_fname_usr_to_disk(struct inode *inode, const struct qstr *iname, struct fscrypt_str *oname) { + /* Leave . and .. alone. */ if (fscrypt_is_dot_dotdot(iname)) { oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; oname->len = iname->len; return oname->len; } + + /* Encrypt the name if we have the key. */ if (inode->i_crypt_info) return fname_encrypt(inode, iname, oname); + /* * Without a proper key, a user is not allowed to modify the filenames * in a directory. Consequently, a user space name cannot be mapped to - * a disk-space name + * a disk-space name. */ return -EACCES; } diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index e0b3281..33296c0 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -255,6 +255,14 @@ void fscrypt_unload_encryption_info(struct inode *inode, } EXPORT_SYMBOL(fscrypt_unload_encryption_info); +/* + * Try to load the fscrypt_info for an encrypted inode into memory. + * + * Return: 0 if the fscrypt_info was loaded or was already loaded, or also 0 if + * the master encryption key is not available (use fscrypt_has_encryption_key() + * to distingush the two cases); or a negative errno value if another error + * occurred. + */ int fscrypt_load_encryption_info(struct inode *inode) { struct fscrypt_info *ci = inode->i_crypt_info; diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h index 9a32d7e..f29dc8c 100644 --- a/include/linux/fscrypto.h +++ b/include/linux/fscrypto.h @@ -36,9 +36,8 @@ #define FS_ENCRYPTION_MODE_AES_256_CTS 4 /** - * Encryption context for inode + * struct fscrypt_context: the on-disk format of an inode's encryption metadata * - * Protector format: * 1 byte: Protector format (1 = this version) * 1 byte: File contents encryption mode * 1 byte: File names encryption mode @@ -67,13 +66,36 @@ struct fscrypt_context { #define FS_KEY_DESC_PREFIX "fscrypt:" #define FS_KEY_DESC_PREFIX_SIZE 8 -/* This is passed in from userspace into the kernel keyring */ +/** + * struct fscrypt_key - payload of a master encryption key, as passed into a + * kernel keyring from userspace + * + * @mode: Currently unused + * @raw: Raw bytes of the key + * @size: Length of the key (number of bytes in @raw actually used) + */ struct fscrypt_key { u32 mode; u8 raw[FS_MAX_KEY_SIZE]; u32 size; } __packed; +/** + * struct fscrypt_info - the cipher, encryption modes, and master key reference + * for an inode + * + * @ci_data_mode: Encryption mode to use for the contents of regular files + * @ci_filename_mode: Encryption mode to use for filenames and symlink targets + * @ci_flags: See FS_POLICY_FLAGS_*. + * @ci_ctfm: Allocated symmetric cipher for this inode, using the inode's + * unique encryption key which is derived from the master key and the + * per-inode nonce. If the inode is a regular file, this cipher uses + * the data encryption mode, whereas if the inode is a directory or + * symlink, this cipher uses the filename encryption mode. + * @ci_keyring_key: Reference to the master key in a kernel keyring, or NULL if + * the test-only "dummy" encryption mode is in effect + * @ci_master_key: The descriptor of the master key, in binary form + */ struct fscrypt_info { u8 ci_data_mode; u8 ci_filename_mode; @@ -86,13 +108,19 @@ struct fscrypt_info { #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define FS_WRITE_PATH_FL 0x00000002 +/** + * struct fscrypt_ctx - holds pages being decrypted or encrypted + * + * This is a reusable structure, not tied to any particular inode. + */ struct fscrypt_ctx { union { - struct { + struct { /* Writing (encryption) */ struct page *bounce_page; /* Ciphertext page */ struct page *control_page; /* Original page */ } w; - struct { + + struct { /* Reading (decryption) */ struct bio *bio; struct work_struct work; } r; @@ -171,7 +199,20 @@ struct fscrypt_name { #define fname_len(p) ((p)->disk_name.len) /* - * crypto opertions for filesystems + * struct fscrypt_operations - crypto operations for filesystems + * + * @get_context - Retrieve the inode's persistent encryption metadata into the + * provided buffer. Semantics are like getxattr(). + * @set_context - Set the inode's persistent encryption metadata. Return 0 or a + * negative errno value. + * @dummy_context - Test whether the inode uses the test-only "dummy" encryption + * mode + * @is_encrypted - Test whether the inode is encrypted + * @empty_dir - Test whether the directory inode is empty + * @max_namelen - Return the maximum length of an unencrypted name we might have + * to encrypt for the inode. Note that for symlinks, we encrypt + * the symlink *target* which typically can be longer than a + * single filename. */ struct fscrypt_operations { int (*get_context)(struct inode *, void *, size_t);