diff options
author | Michael Halcrow <mhalcrow@google.com> | 2015-04-11 07:48:01 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-04-11 07:48:01 -0400 |
commit | 9bd8212f981ea6375911fe055382ad7529be5b28 (patch) | |
tree | 1ff145c6d5986d4687230ca4918ae8d5dece40bc /fs/ext4 | |
parent | 887e2c452255fbfdc8bdb891ff2066fb26908466 (diff) |
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>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/Makefile | 1 | ||||
-rw-r--r-- | fs/ext4/crypto_policy.c | 167 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 15 | ||||
-rw-r--r-- | fs/ext4/ext4_crypto.h | 49 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 85 |
5 files changed, 317 insertions, 0 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index cd6f50fce278..3886ee45f556 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile | |||
@@ -12,3 +12,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ | |||
12 | 12 | ||
13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o | 13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o |
14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o | 14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o |
15 | ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o | ||
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c new file mode 100644 index 000000000000..532b69c0afab --- /dev/null +++ b/fs/ext4/crypto_policy.c | |||
@@ -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 | } | ||
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 180111de2302..ab873aa9955e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -589,6 +589,8 @@ enum { | |||
589 | #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 | 589 | #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 |
590 | #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 | 590 | #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 |
591 | 591 | ||
592 | #include "ext4_crypto.h" | ||
593 | |||
592 | /* | 594 | /* |
593 | * ioctl commands | 595 | * ioctl commands |
594 | */ | 596 | */ |
@@ -610,6 +612,9 @@ enum { | |||
610 | #define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) | 612 | #define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) |
611 | #define EXT4_IOC_SWAP_BOOT _IO('f', 17) | 613 | #define EXT4_IOC_SWAP_BOOT _IO('f', 17) |
612 | #define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18) | 614 | #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) | ||
613 | 618 | ||
614 | #if defined(__KERNEL__) && defined(CONFIG_COMPAT) | 619 | #if defined(__KERNEL__) && defined(CONFIG_COMPAT) |
615 | /* | 620 | /* |
@@ -2011,6 +2016,16 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb, | |||
2011 | struct ext4_group_desc *gdp); | 2016 | struct ext4_group_desc *gdp); |
2012 | ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); | 2017 | ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); |
2013 | 2018 | ||
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 | |||
2014 | /* dir.c */ | 2029 | /* dir.c */ |
2015 | extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, | 2030 | extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, |
2016 | struct file *, | 2031 | struct file *, |
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h new file mode 100644 index 000000000000..a69d2ba54bee --- /dev/null +++ b/fs/ext4/ext4_crypto.h | |||
@@ -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 */ | ||
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index f1aa32c2277c..2cb9e178d1c5 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/compat.h> | 13 | #include <linux/compat.h> |
14 | #include <linux/mount.h> | 14 | #include <linux/mount.h> |
15 | #include <linux/file.h> | 15 | #include <linux/file.h> |
16 | #include <linux/random.h> | ||
16 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
17 | #include "ext4_jbd2.h" | 18 | #include "ext4_jbd2.h" |
18 | #include "ext4.h" | 19 | #include "ext4.h" |
@@ -195,6 +196,16 @@ journal_err_out: | |||
195 | return err; | 196 | return err; |
196 | } | 197 | } |
197 | 198 | ||
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 | |||
198 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 209 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
199 | { | 210 | { |
200 | struct inode *inode = file_inode(filp); | 211 | struct inode *inode = file_inode(filp); |
@@ -614,7 +625,78 @@ resizefs_out: | |||
614 | } | 625 | } |
615 | case EXT4_IOC_PRECACHE_EXTENTS: | 626 | case EXT4_IOC_PRECACHE_EXTENTS: |
616 | return ext4_ext_precache(inode); | 627 | 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 | } | ||
617 | 639 | ||
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 | } | ||
618 | default: | 700 | default: |
619 | return -ENOTTY; | 701 | return -ENOTTY; |
620 | } | 702 | } |
@@ -679,6 +761,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
679 | case FITRIM: | 761 | case FITRIM: |
680 | case EXT4_IOC_RESIZE_FS: | 762 | case EXT4_IOC_RESIZE_FS: |
681 | case EXT4_IOC_PRECACHE_EXTENTS: | 763 | 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: | ||
682 | break; | 767 | break; |
683 | default: | 768 | default: |
684 | return -ENOIOCTLCMD; | 769 | return -ENOIOCTLCMD; |