Skip to content

Commit 9bd8212

Browse files
mhalcrow-googletytso
authored andcommitted
ext4 crypto: add encryption policy and password salt support
Signed-off-by: Michael Halcrow <mhalcrow@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
1 parent 887e2c4 commit 9bd8212

File tree

5 files changed

+317
-0
lines changed

5 files changed

+317
-0
lines changed

fs/ext4/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
1212

1313
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
1414
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
15+
ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o

fs/ext4/crypto_policy.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* linux/fs/ext4/crypto_policy.c
3+
*
4+
* Copyright (C) 2015, Google, Inc.
5+
*
6+
* This contains encryption policy functions for ext4
7+
*
8+
* Written by Michael Halcrow, 2015.
9+
*/
10+
11+
#include <linux/random.h>
12+
#include <linux/string.h>
13+
#include <linux/types.h>
14+
15+
#include "ext4.h"
16+
#include "xattr.h"
17+
18+
static int ext4_inode_has_encryption_context(struct inode *inode)
19+
{
20+
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
21+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
22+
return (res > 0);
23+
}
24+
25+
/*
26+
* check whether the policy is consistent with the encryption context
27+
* for the inode
28+
*/
29+
static int ext4_is_encryption_context_consistent_with_policy(
30+
struct inode *inode, const struct ext4_encryption_policy *policy)
31+
{
32+
struct ext4_encryption_context ctx;
33+
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
34+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
35+
sizeof(ctx));
36+
if (res != sizeof(ctx))
37+
return 0;
38+
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
39+
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
40+
(ctx.contents_encryption_mode ==
41+
policy->contents_encryption_mode) &&
42+
(ctx.filenames_encryption_mode ==
43+
policy->filenames_encryption_mode));
44+
}
45+
46+
static int ext4_create_encryption_context_from_policy(
47+
struct inode *inode, const struct ext4_encryption_policy *policy)
48+
{
49+
struct ext4_encryption_context ctx;
50+
int res = 0;
51+
52+
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
53+
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
54+
EXT4_KEY_DESCRIPTOR_SIZE);
55+
ctx.contents_encryption_mode = policy->contents_encryption_mode;
56+
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
57+
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
58+
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
59+
60+
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
61+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
62+
sizeof(ctx), 0);
63+
if (!res)
64+
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
65+
return res;
66+
}
67+
68+
int ext4_process_policy(const struct ext4_encryption_policy *policy,
69+
struct inode *inode)
70+
{
71+
if (policy->version != 0)
72+
return -EINVAL;
73+
74+
if (!ext4_inode_has_encryption_context(inode)) {
75+
if (!ext4_empty_dir(inode))
76+
return -ENOTEMPTY;
77+
return ext4_create_encryption_context_from_policy(inode,
78+
policy);
79+
}
80+
81+
if (ext4_is_encryption_context_consistent_with_policy(inode, policy))
82+
return 0;
83+
84+
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
85+
__func__);
86+
return -EINVAL;
87+
}
88+
89+
int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
90+
{
91+
struct ext4_encryption_context ctx;
92+
93+
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
94+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
95+
&ctx, sizeof(ctx));
96+
if (res != sizeof(ctx))
97+
return -ENOENT;
98+
if (ctx.format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1)
99+
return -EINVAL;
100+
policy->version = 0;
101+
policy->contents_encryption_mode = ctx.contents_encryption_mode;
102+
policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
103+
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
104+
EXT4_KEY_DESCRIPTOR_SIZE);
105+
return 0;
106+
}
107+
108+
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
109+
struct inode *child)
110+
{
111+
struct ext4_encryption_context parent_ctx, child_ctx;
112+
int res;
113+
114+
if ((parent == NULL) || (child == NULL)) {
115+
pr_err("parent %p child %p\n", parent, child);
116+
BUG_ON(1);
117+
}
118+
/* no restrictions if the parent directory is not encrypted */
119+
if (!ext4_encrypted_inode(parent))
120+
return 1;
121+
res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
122+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
123+
&parent_ctx, sizeof(parent_ctx));
124+
if (res != sizeof(parent_ctx))
125+
return 0;
126+
/* if the child directory is not encrypted, this is always a problem */
127+
if (!ext4_encrypted_inode(child))
128+
return 0;
129+
res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION,
130+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
131+
&child_ctx, sizeof(child_ctx));
132+
if (res != sizeof(child_ctx))
133+
return 0;
134+
return (memcmp(parent_ctx.master_key_descriptor,
135+
child_ctx.master_key_descriptor,
136+
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
137+
(parent_ctx.contents_encryption_mode ==
138+
child_ctx.contents_encryption_mode) &&
139+
(parent_ctx.filenames_encryption_mode ==
140+
child_ctx.filenames_encryption_mode));
141+
}
142+
143+
/**
144+
* ext4_inherit_context() - Sets a child context from its parent
145+
* @parent: Parent inode from which the context is inherited.
146+
* @child: Child inode that inherits the context from @parent.
147+
*
148+
* Return: Zero on success, non-zero otherwise
149+
*/
150+
int ext4_inherit_context(struct inode *parent, struct inode *child)
151+
{
152+
struct ext4_encryption_context ctx;
153+
int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
154+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
155+
&ctx, sizeof(ctx));
156+
157+
if (res != sizeof(ctx))
158+
return -ENOENT;
159+
160+
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
161+
res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
162+
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
163+
sizeof(ctx), 0);
164+
if (!res)
165+
ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
166+
return res;
167+
}

fs/ext4/ext4.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,8 @@ enum {
589589
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
590590
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
591591

592+
#include "ext4_crypto.h"
593+
592594
/*
593595
* ioctl commands
594596
*/
@@ -610,6 +612,9 @@ enum {
610612
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
611613
#define EXT4_IOC_SWAP_BOOT _IO('f', 17)
612614
#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18)
615+
#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
616+
#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
617+
#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
613618

614619
#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
615620
/*
@@ -2011,6 +2016,16 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
20112016
struct ext4_group_desc *gdp);
20122017
ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
20132018

2019+
/* crypto_policy.c */
2020+
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
2021+
struct inode *child);
2022+
int ext4_inherit_context(struct inode *parent, struct inode *child);
2023+
void ext4_to_hex(char *dst, char *src, size_t src_size);
2024+
int ext4_process_policy(const struct ext4_encryption_policy *policy,
2025+
struct inode *inode);
2026+
int ext4_get_policy(struct inode *inode,
2027+
struct ext4_encryption_policy *policy);
2028+
20142029
/* dir.c */
20152030
extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
20162031
struct file *,

fs/ext4/ext4_crypto.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* linux/fs/ext4/ext4_crypto.h
3+
*
4+
* Copyright (C) 2015, Google, Inc.
5+
*
6+
* This contains encryption header content for ext4
7+
*
8+
* Written by Michael Halcrow, 2015.
9+
*/
10+
11+
#ifndef _EXT4_CRYPTO_H
12+
#define _EXT4_CRYPTO_H
13+
14+
#include <linux/fs.h>
15+
16+
#define EXT4_KEY_DESCRIPTOR_SIZE 8
17+
18+
/* Policy provided via an ioctl on the topmost directory */
19+
struct ext4_encryption_policy {
20+
char version;
21+
char contents_encryption_mode;
22+
char filenames_encryption_mode;
23+
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
24+
} __attribute__((__packed__));
25+
26+
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
27+
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
28+
29+
/**
30+
* Encryption context for inode
31+
*
32+
* Protector format:
33+
* 1 byte: Protector format (1 = this version)
34+
* 1 byte: File contents encryption mode
35+
* 1 byte: File names encryption mode
36+
* 1 byte: Reserved
37+
* 8 bytes: Master Key descriptor
38+
* 16 bytes: Encryption Key derivation nonce
39+
*/
40+
struct ext4_encryption_context {
41+
char format;
42+
char contents_encryption_mode;
43+
char filenames_encryption_mode;
44+
char reserved;
45+
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
46+
char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
47+
} __attribute__((__packed__));
48+
49+
#endif /* _EXT4_CRYPTO_H */

fs/ext4/ioctl.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/compat.h>
1414
#include <linux/mount.h>
1515
#include <linux/file.h>
16+
#include <linux/random.h>
1617
#include <asm/uaccess.h>
1718
#include "ext4_jbd2.h"
1819
#include "ext4.h"
@@ -195,6 +196,16 @@ static long swap_inode_boot_loader(struct super_block *sb,
195196
return err;
196197
}
197198

199+
static int uuid_is_zero(__u8 u[16])
200+
{
201+
int i;
202+
203+
for (i = 0; i < 16; i++)
204+
if (u[i])
205+
return 0;
206+
return 1;
207+
}
208+
198209
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
199210
{
200211
struct inode *inode = file_inode(filp);
@@ -614,7 +625,78 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
614625
}
615626
case EXT4_IOC_PRECACHE_EXTENTS:
616627
return ext4_ext_precache(inode);
628+
case EXT4_IOC_SET_ENCRYPTION_POLICY: {
629+
#ifdef CONFIG_EXT4_FS_ENCRYPTION
630+
struct ext4_encryption_policy policy;
631+
int err = 0;
632+
633+
if (copy_from_user(&policy,
634+
(struct ext4_encryption_policy __user *)arg,
635+
sizeof(policy))) {
636+
err = -EFAULT;
637+
goto encryption_policy_out;
638+
}
617639

640+
err = ext4_process_policy(&policy, inode);
641+
encryption_policy_out:
642+
return err;
643+
#else
644+
return -EOPNOTSUPP;
645+
#endif
646+
}
647+
case EXT4_IOC_GET_ENCRYPTION_PWSALT: {
648+
int err, err2;
649+
struct ext4_sb_info *sbi = EXT4_SB(sb);
650+
handle_t *handle;
651+
652+
if (!ext4_sb_has_crypto(sb))
653+
return -EOPNOTSUPP;
654+
if (uuid_is_zero(sbi->s_es->s_encrypt_pw_salt)) {
655+
err = mnt_want_write_file(filp);
656+
if (err)
657+
return err;
658+
handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1);
659+
if (IS_ERR(handle)) {
660+
err = PTR_ERR(handle);
661+
goto pwsalt_err_exit;
662+
}
663+
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
664+
if (err)
665+
goto pwsalt_err_journal;
666+
generate_random_uuid(sbi->s_es->s_encrypt_pw_salt);
667+
err = ext4_handle_dirty_metadata(handle, NULL,
668+
sbi->s_sbh);
669+
pwsalt_err_journal:
670+
err2 = ext4_journal_stop(handle);
671+
if (err2 && !err)
672+
err = err2;
673+
pwsalt_err_exit:
674+
mnt_drop_write_file(filp);
675+
if (err)
676+
return err;
677+
}
678+
if (copy_to_user((void *) arg, sbi->s_es->s_encrypt_pw_salt,
679+
16))
680+
return -EFAULT;
681+
return 0;
682+
}
683+
case EXT4_IOC_GET_ENCRYPTION_POLICY: {
684+
#ifdef CONFIG_EXT4_FS_ENCRYPTION
685+
struct ext4_encryption_policy policy;
686+
int err = 0;
687+
688+
if (!ext4_encrypted_inode(inode))
689+
return -ENOENT;
690+
err = ext4_get_policy(inode, &policy);
691+
if (err)
692+
return err;
693+
if (copy_to_user((void *)arg, &policy, sizeof(policy)))
694+
return -EFAULT;
695+
return 0;
696+
#else
697+
return -EOPNOTSUPP;
698+
#endif
699+
}
618700
default:
619701
return -ENOTTY;
620702
}
@@ -679,6 +761,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
679761
case FITRIM:
680762
case EXT4_IOC_RESIZE_FS:
681763
case EXT4_IOC_PRECACHE_EXTENTS:
764+
case EXT4_IOC_SET_ENCRYPTION_POLICY:
765+
case EXT4_IOC_GET_ENCRYPTION_PWSALT:
766+
case EXT4_IOC_GET_ENCRYPTION_POLICY:
682767
break;
683768
default:
684769
return -ENOIOCTLCMD;

0 commit comments

Comments
 (0)