Signed-off-by: Eric Biggers <ebiggers3@xxxxxxxxx> --- 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); -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html