diff options
author | Theodore Ts'o <tytso@mit.edu> | 2015-04-16 01:55:00 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-04-16 01:55:00 -0400 |
commit | f348c252320b98e11176074fe04223f22bddaf0d (patch) | |
tree | 71fe0a1c86bd14f661a7e823e3b330adfc2719e3 /fs | |
parent | 4461471107b79bee16c497c9f7f69fa26126ae5b (diff) |
ext4 crypto: add symlink encryption
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/ext4_crypto.h | 20 | ||||
-rw-r--r-- | fs/ext4/inode.c | 5 | ||||
-rw-r--r-- | fs/ext4/namei.c | 85 | ||||
-rw-r--r-- | fs/ext4/symlink.c | 96 |
5 files changed, 184 insertions, 23 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5146e67e8d51..86d15706d27a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2233,6 +2233,7 @@ extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, | |||
2233 | extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); | 2233 | extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); |
2234 | 2234 | ||
2235 | /* inode.c */ | 2235 | /* inode.c */ |
2236 | int ext4_inode_is_fast_symlink(struct inode *inode); | ||
2236 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); | 2237 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); |
2237 | struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); | 2238 | struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); |
2238 | int ext4_get_block_write(struct inode *inode, sector_t iblock, | 2239 | int ext4_get_block_write(struct inode *inode, sector_t iblock, |
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index f7d46e8dc9d3..c2ba35a914b6 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h | |||
@@ -124,4 +124,24 @@ struct ext4_fname_crypto_ctx { | |||
124 | unsigned ctfm_key_is_ready : 1; | 124 | unsigned ctfm_key_is_ready : 1; |
125 | }; | 125 | }; |
126 | 126 | ||
127 | /** | ||
128 | * For encrypted symlinks, the ciphertext length is stored at the beginning | ||
129 | * of the string in little-endian format. | ||
130 | */ | ||
131 | struct ext4_encrypted_symlink_data { | ||
132 | __le16 len; | ||
133 | char encrypted_path[1]; | ||
134 | } __attribute__((__packed__)); | ||
135 | |||
136 | /** | ||
137 | * This function is used to calculate the disk space required to | ||
138 | * store a filename of length l in encrypted symlink format. | ||
139 | */ | ||
140 | static inline u32 encrypted_symlink_data_len(u32 l) | ||
141 | { | ||
142 | if (l < EXT4_CRYPTO_BLOCK_SIZE) | ||
143 | l = EXT4_CRYPTO_BLOCK_SIZE; | ||
144 | return (l + sizeof(struct ext4_encrypted_symlink_data) - 1); | ||
145 | } | ||
146 | |||
127 | #endif /* _EXT4_CRYPTO_H */ | 147 | #endif /* _EXT4_CRYPTO_H */ |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8b4fe626919a..f6b35d8a4a5b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -139,7 +139,7 @@ static int ext4_meta_trans_blocks(struct inode *inode, int lblocks, | |||
139 | /* | 139 | /* |
140 | * Test whether an inode is a fast symlink. | 140 | * Test whether an inode is a fast symlink. |
141 | */ | 141 | */ |
142 | static int ext4_inode_is_fast_symlink(struct inode *inode) | 142 | int ext4_inode_is_fast_symlink(struct inode *inode) |
143 | { | 143 | { |
144 | int ea_blocks = EXT4_I(inode)->i_file_acl ? | 144 | int ea_blocks = EXT4_I(inode)->i_file_acl ? |
145 | EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0; | 145 | EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0; |
@@ -4215,7 +4215,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
4215 | inode->i_op = &ext4_dir_inode_operations; | 4215 | inode->i_op = &ext4_dir_inode_operations; |
4216 | inode->i_fop = &ext4_dir_operations; | 4216 | inode->i_fop = &ext4_dir_operations; |
4217 | } else if (S_ISLNK(inode->i_mode)) { | 4217 | } else if (S_ISLNK(inode->i_mode)) { |
4218 | if (ext4_inode_is_fast_symlink(inode)) { | 4218 | if (ext4_inode_is_fast_symlink(inode) && |
4219 | !ext4_encrypted_inode(inode)) { | ||
4219 | inode->i_op = &ext4_fast_symlink_inode_operations; | 4220 | inode->i_op = &ext4_fast_symlink_inode_operations; |
4220 | nd_terminate_link(ei->i_data, inode->i_size, | 4221 | nd_terminate_link(ei->i_data, inode->i_size, |
4221 | sizeof(ei->i_data) - 1); | 4222 | sizeof(ei->i_data) - 1); |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4c84db862891..d201426b8d39 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -3193,16 +3193,24 @@ static int ext4_symlink(struct inode *dir, | |||
3193 | { | 3193 | { |
3194 | handle_t *handle; | 3194 | handle_t *handle; |
3195 | struct inode *inode; | 3195 | struct inode *inode; |
3196 | int l, err, retries = 0; | 3196 | int err, len = strlen(symname); |
3197 | int credits; | 3197 | int credits; |
3198 | bool encryption_required; | ||
3199 | struct ext4_str disk_link; | ||
3200 | struct ext4_encrypted_symlink_data *sd = NULL; | ||
3198 | 3201 | ||
3199 | l = strlen(symname)+1; | 3202 | disk_link.len = len + 1; |
3200 | if (l > dir->i_sb->s_blocksize) | 3203 | disk_link.name = (char *) symname; |
3204 | |||
3205 | encryption_required = ext4_encrypted_inode(dir); | ||
3206 | if (encryption_required) | ||
3207 | disk_link.len = encrypted_symlink_data_len(len) + 1; | ||
3208 | if (disk_link.len > dir->i_sb->s_blocksize) | ||
3201 | return -ENAMETOOLONG; | 3209 | return -ENAMETOOLONG; |
3202 | 3210 | ||
3203 | dquot_initialize(dir); | 3211 | dquot_initialize(dir); |
3204 | 3212 | ||
3205 | if (l > EXT4_N_BLOCKS * 4) { | 3213 | if ((disk_link.len > EXT4_N_BLOCKS * 4)) { |
3206 | /* | 3214 | /* |
3207 | * For non-fast symlinks, we just allocate inode and put it on | 3215 | * For non-fast symlinks, we just allocate inode and put it on |
3208 | * orphan list in the first transaction => we need bitmap, | 3216 | * orphan list in the first transaction => we need bitmap, |
@@ -3221,16 +3229,49 @@ static int ext4_symlink(struct inode *dir, | |||
3221 | credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + | 3229 | credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + |
3222 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; | 3230 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; |
3223 | } | 3231 | } |
3224 | retry: | 3232 | |
3225 | inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, | 3233 | inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, |
3226 | &dentry->d_name, 0, NULL, | 3234 | &dentry->d_name, 0, NULL, |
3227 | EXT4_HT_DIR, credits); | 3235 | EXT4_HT_DIR, credits); |
3228 | handle = ext4_journal_current_handle(); | 3236 | handle = ext4_journal_current_handle(); |
3229 | err = PTR_ERR(inode); | 3237 | if (IS_ERR(inode)) { |
3230 | if (IS_ERR(inode)) | 3238 | if (handle) |
3231 | goto out_stop; | 3239 | ext4_journal_stop(handle); |
3240 | return PTR_ERR(inode); | ||
3241 | } | ||
3242 | |||
3243 | if (encryption_required) { | ||
3244 | struct ext4_fname_crypto_ctx *ctx = NULL; | ||
3245 | struct qstr istr; | ||
3246 | struct ext4_str ostr; | ||
3247 | |||
3248 | sd = kzalloc(disk_link.len, GFP_NOFS); | ||
3249 | if (!sd) { | ||
3250 | err = -ENOMEM; | ||
3251 | goto err_drop_inode; | ||
3252 | } | ||
3253 | err = ext4_inherit_context(dir, inode); | ||
3254 | if (err) | ||
3255 | goto err_drop_inode; | ||
3256 | ctx = ext4_get_fname_crypto_ctx(inode, | ||
3257 | inode->i_sb->s_blocksize); | ||
3258 | if (IS_ERR_OR_NULL(ctx)) { | ||
3259 | /* We just set the policy, so ctx should not be NULL */ | ||
3260 | err = (ctx == NULL) ? -EIO : PTR_ERR(ctx); | ||
3261 | goto err_drop_inode; | ||
3262 | } | ||
3263 | istr.name = (const unsigned char *) symname; | ||
3264 | istr.len = len; | ||
3265 | ostr.name = sd->encrypted_path; | ||
3266 | err = ext4_fname_usr_to_disk(ctx, &istr, &ostr); | ||
3267 | ext4_put_fname_crypto_ctx(&ctx); | ||
3268 | if (err < 0) | ||
3269 | goto err_drop_inode; | ||
3270 | sd->len = cpu_to_le16(ostr.len); | ||
3271 | disk_link.name = (char *) sd; | ||
3272 | } | ||
3232 | 3273 | ||
3233 | if (l > EXT4_N_BLOCKS * 4) { | 3274 | if ((disk_link.len > EXT4_N_BLOCKS * 4)) { |
3234 | inode->i_op = &ext4_symlink_inode_operations; | 3275 | inode->i_op = &ext4_symlink_inode_operations; |
3235 | ext4_set_aops(inode); | 3276 | ext4_set_aops(inode); |
3236 | /* | 3277 | /* |
@@ -3246,9 +3287,10 @@ retry: | |||
3246 | drop_nlink(inode); | 3287 | drop_nlink(inode); |
3247 | err = ext4_orphan_add(handle, inode); | 3288 | err = ext4_orphan_add(handle, inode); |
3248 | ext4_journal_stop(handle); | 3289 | ext4_journal_stop(handle); |
3290 | handle = NULL; | ||
3249 | if (err) | 3291 | if (err) |
3250 | goto err_drop_inode; | 3292 | goto err_drop_inode; |
3251 | err = __page_symlink(inode, symname, l, 1); | 3293 | err = __page_symlink(inode, disk_link.name, disk_link.len, 1); |
3252 | if (err) | 3294 | if (err) |
3253 | goto err_drop_inode; | 3295 | goto err_drop_inode; |
3254 | /* | 3296 | /* |
@@ -3260,34 +3302,37 @@ retry: | |||
3260 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); | 3302 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); |
3261 | if (IS_ERR(handle)) { | 3303 | if (IS_ERR(handle)) { |
3262 | err = PTR_ERR(handle); | 3304 | err = PTR_ERR(handle); |
3305 | handle = NULL; | ||
3263 | goto err_drop_inode; | 3306 | goto err_drop_inode; |
3264 | } | 3307 | } |
3265 | set_nlink(inode, 1); | 3308 | set_nlink(inode, 1); |
3266 | err = ext4_orphan_del(handle, inode); | 3309 | err = ext4_orphan_del(handle, inode); |
3267 | if (err) { | 3310 | if (err) |
3268 | ext4_journal_stop(handle); | ||
3269 | clear_nlink(inode); | ||
3270 | goto err_drop_inode; | 3311 | goto err_drop_inode; |
3271 | } | ||
3272 | } else { | 3312 | } else { |
3273 | /* clear the extent format for fast symlink */ | 3313 | /* clear the extent format for fast symlink */ |
3274 | ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); | 3314 | ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); |
3275 | inode->i_op = &ext4_fast_symlink_inode_operations; | 3315 | inode->i_op = encryption_required ? |
3276 | memcpy((char *)&EXT4_I(inode)->i_data, symname, l); | 3316 | &ext4_symlink_inode_operations : |
3277 | inode->i_size = l-1; | 3317 | &ext4_fast_symlink_inode_operations; |
3318 | memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, | ||
3319 | disk_link.len); | ||
3320 | inode->i_size = disk_link.len - 1; | ||
3278 | } | 3321 | } |
3279 | EXT4_I(inode)->i_disksize = inode->i_size; | 3322 | EXT4_I(inode)->i_disksize = inode->i_size; |
3280 | err = ext4_add_nondir(handle, dentry, inode); | 3323 | err = ext4_add_nondir(handle, dentry, inode); |
3281 | if (!err && IS_DIRSYNC(dir)) | 3324 | if (!err && IS_DIRSYNC(dir)) |
3282 | ext4_handle_sync(handle); | 3325 | ext4_handle_sync(handle); |
3283 | 3326 | ||
3284 | out_stop: | ||
3285 | if (handle) | 3327 | if (handle) |
3286 | ext4_journal_stop(handle); | 3328 | ext4_journal_stop(handle); |
3287 | if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) | 3329 | kfree(sd); |
3288 | goto retry; | ||
3289 | return err; | 3330 | return err; |
3290 | err_drop_inode: | 3331 | err_drop_inode: |
3332 | if (handle) | ||
3333 | ext4_journal_stop(handle); | ||
3334 | kfree(sd); | ||
3335 | clear_nlink(inode); | ||
3291 | unlock_new_inode(inode); | 3336 | unlock_new_inode(inode); |
3292 | iput(inode); | 3337 | iput(inode); |
3293 | return err; | 3338 | return err; |
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 489b34333ea4..136ca0e911fd 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c | |||
@@ -22,8 +22,97 @@ | |||
22 | #include "ext4.h" | 22 | #include "ext4.h" |
23 | #include "xattr.h" | 23 | #include "xattr.h" |
24 | 24 | ||
25 | #ifdef CONFIG_EXT4_FS_ENCRYPTION | ||
25 | static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) | 26 | static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) |
26 | { | 27 | { |
28 | struct page *cpage = NULL; | ||
29 | char *caddr, *paddr = NULL; | ||
30 | struct ext4_str cstr, pstr; | ||
31 | struct inode *inode = dentry->d_inode; | ||
32 | struct ext4_fname_crypto_ctx *ctx = NULL; | ||
33 | struct ext4_encrypted_symlink_data *sd; | ||
34 | loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); | ||
35 | int res; | ||
36 | u32 plen, max_size = inode->i_sb->s_blocksize; | ||
37 | |||
38 | if (!ext4_encrypted_inode(inode)) | ||
39 | return page_follow_link_light(dentry, nd); | ||
40 | |||
41 | ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize); | ||
42 | if (IS_ERR(ctx)) | ||
43 | return ctx; | ||
44 | |||
45 | if (ext4_inode_is_fast_symlink(inode)) { | ||
46 | caddr = (char *) EXT4_I(dentry->d_inode)->i_data; | ||
47 | max_size = sizeof(EXT4_I(dentry->d_inode)->i_data); | ||
48 | } else { | ||
49 | cpage = read_mapping_page(inode->i_mapping, 0, NULL); | ||
50 | if (IS_ERR(cpage)) { | ||
51 | ext4_put_fname_crypto_ctx(&ctx); | ||
52 | return cpage; | ||
53 | } | ||
54 | caddr = kmap(cpage); | ||
55 | caddr[size] = 0; | ||
56 | } | ||
57 | |||
58 | /* Symlink is encrypted */ | ||
59 | sd = (struct ext4_encrypted_symlink_data *)caddr; | ||
60 | cstr.name = sd->encrypted_path; | ||
61 | cstr.len = le32_to_cpu(sd->len); | ||
62 | if ((cstr.len + | ||
63 | sizeof(struct ext4_encrypted_symlink_data) - 1) > | ||
64 | max_size) { | ||
65 | /* Symlink data on the disk is corrupted */ | ||
66 | res = -EIO; | ||
67 | goto errout; | ||
68 | } | ||
69 | plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) ? | ||
70 | EXT4_FNAME_CRYPTO_DIGEST_SIZE*2 : cstr.len; | ||
71 | paddr = kmalloc(plen + 1, GFP_NOFS); | ||
72 | if (!paddr) { | ||
73 | res = -ENOMEM; | ||
74 | goto errout; | ||
75 | } | ||
76 | pstr.name = paddr; | ||
77 | res = _ext4_fname_disk_to_usr(ctx, &cstr, &pstr); | ||
78 | if (res < 0) | ||
79 | goto errout; | ||
80 | /* Null-terminate the name */ | ||
81 | if (res <= plen) | ||
82 | paddr[res] = '\0'; | ||
83 | nd_set_link(nd, paddr); | ||
84 | ext4_put_fname_crypto_ctx(&ctx); | ||
85 | if (cpage) { | ||
86 | kunmap(cpage); | ||
87 | page_cache_release(cpage); | ||
88 | } | ||
89 | return NULL; | ||
90 | errout: | ||
91 | ext4_put_fname_crypto_ctx(&ctx); | ||
92 | if (cpage) { | ||
93 | kunmap(cpage); | ||
94 | page_cache_release(cpage); | ||
95 | } | ||
96 | kfree(paddr); | ||
97 | return ERR_PTR(res); | ||
98 | } | ||
99 | |||
100 | static void ext4_put_link(struct dentry *dentry, struct nameidata *nd, | ||
101 | void *cookie) | ||
102 | { | ||
103 | struct page *page = cookie; | ||
104 | |||
105 | if (!page) { | ||
106 | kfree(nd_get_link(nd)); | ||
107 | } else { | ||
108 | kunmap(page); | ||
109 | page_cache_release(page); | ||
110 | } | ||
111 | } | ||
112 | #endif | ||
113 | |||
114 | static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) | ||
115 | { | ||
27 | struct ext4_inode_info *ei = EXT4_I(dentry->d_inode); | 116 | struct ext4_inode_info *ei = EXT4_I(dentry->d_inode); |
28 | nd_set_link(nd, (char *) ei->i_data); | 117 | nd_set_link(nd, (char *) ei->i_data); |
29 | return NULL; | 118 | return NULL; |
@@ -31,8 +120,13 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
31 | 120 | ||
32 | const struct inode_operations ext4_symlink_inode_operations = { | 121 | const struct inode_operations ext4_symlink_inode_operations = { |
33 | .readlink = generic_readlink, | 122 | .readlink = generic_readlink, |
123 | #ifdef CONFIG_EXT4_FS_ENCRYPTION | ||
124 | .follow_link = ext4_follow_link, | ||
125 | .put_link = ext4_put_link, | ||
126 | #else | ||
34 | .follow_link = page_follow_link_light, | 127 | .follow_link = page_follow_link_light, |
35 | .put_link = page_put_link, | 128 | .put_link = page_put_link, |
129 | #endif | ||
36 | .setattr = ext4_setattr, | 130 | .setattr = ext4_setattr, |
37 | .setxattr = generic_setxattr, | 131 | .setxattr = generic_setxattr, |
38 | .getxattr = generic_getxattr, | 132 | .getxattr = generic_getxattr, |
@@ -42,7 +136,7 @@ const struct inode_operations ext4_symlink_inode_operations = { | |||
42 | 136 | ||
43 | const struct inode_operations ext4_fast_symlink_inode_operations = { | 137 | const struct inode_operations ext4_fast_symlink_inode_operations = { |
44 | .readlink = generic_readlink, | 138 | .readlink = generic_readlink, |
45 | .follow_link = ext4_follow_link, | 139 | .follow_link = ext4_follow_fast_link, |
46 | .setattr = ext4_setattr, | 140 | .setattr = ext4_setattr, |
47 | .setxattr = generic_setxattr, | 141 | .setxattr = generic_setxattr, |
48 | .getxattr = generic_getxattr, | 142 | .getxattr = generic_getxattr, |