Skip to content

Commit 79f2f6a

Browse files
jtlaytonidryomov
authored andcommitted
ceph: create symlinks with encrypted and base64-encoded targets
When creating symlinks in encrypted directories, encrypt and base64-encode the target with the new inode's key before sending to the MDS. When filling a symlinked inode, base64-decode it into a buffer that we'll keep in ci->i_symlink. When get_link is called, decrypt the buffer into a new one that will hang off i_link. Signed-off-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Xiubo Li <xiubli@redhat.com> Reviewed-and-tested-by: Luís Henriques <lhenriques@suse.de> Reviewed-by: Milind Changire <mchangir@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
1 parent af9ffa6 commit 79f2f6a

2 files changed

Lines changed: 150 additions & 17 deletions

File tree

fs/ceph/dir.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,43 @@ static int ceph_create(struct mnt_idmap *idmap, struct inode *dir,
948948
return ceph_mknod(idmap, dir, dentry, mode, 0);
949949
}
950950

951+
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
952+
static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
953+
const char *dest)
954+
{
955+
int err;
956+
int len = strlen(dest);
957+
struct fscrypt_str osd_link = FSTR_INIT(NULL, 0);
958+
959+
err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX,
960+
&osd_link);
961+
if (err)
962+
goto out;
963+
964+
err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link);
965+
if (err)
966+
goto out;
967+
968+
req->r_path2 = kmalloc(CEPH_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL);
969+
if (!req->r_path2) {
970+
err = -ENOMEM;
971+
goto out;
972+
}
973+
974+
len = ceph_base64_encode(osd_link.name, osd_link.len, req->r_path2);
975+
req->r_path2[len] = '\0';
976+
out:
977+
fscrypt_fname_free_buffer(&osd_link);
978+
return err;
979+
}
980+
#else
981+
static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
982+
const char *dest)
983+
{
984+
return -EOPNOTSUPP;
985+
}
986+
#endif
987+
951988
static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
952989
struct dentry *dentry, const char *dest)
953990
{
@@ -983,14 +1020,21 @@ static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
9831020
goto out_req;
9841021
}
9851022

986-
req->r_path2 = kstrdup(dest, GFP_KERNEL);
987-
if (!req->r_path2) {
988-
err = -ENOMEM;
989-
goto out_req;
990-
}
9911023
req->r_parent = dir;
9921024
ihold(dir);
9931025

1026+
if (IS_ENCRYPTED(req->r_new_inode)) {
1027+
err = prep_encrypted_symlink_target(req, dest);
1028+
if (err)
1029+
goto out_req;
1030+
} else {
1031+
req->r_path2 = kstrdup(dest, GFP_KERNEL);
1032+
if (!req->r_path2) {
1033+
err = -ENOMEM;
1034+
goto out_req;
1035+
}
1036+
}
1037+
9941038
set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
9951039
req->r_dentry = dget(dentry);
9961040
req->r_num_caps = 2;

fs/ceph/inode.c

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
*/
3636

3737
static const struct inode_operations ceph_symlink_iops;
38+
static const struct inode_operations ceph_encrypted_symlink_iops;
3839

3940
static void ceph_inode_work(struct work_struct *work);
4041

@@ -640,6 +641,7 @@ void ceph_free_inode(struct inode *inode)
640641
#ifdef CONFIG_FS_ENCRYPTION
641642
kfree(ci->fscrypt_auth);
642643
#endif
644+
fscrypt_free_inode(inode);
643645
kmem_cache_free(ceph_inode_cachep, ci);
644646
}
645647

@@ -837,6 +839,34 @@ void ceph_fill_file_time(struct inode *inode, int issued,
837839
inode, time_warp_seq, ci->i_time_warp_seq);
838840
}
839841

842+
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
843+
static int decode_encrypted_symlink(const char *encsym, int enclen, u8 **decsym)
844+
{
845+
int declen;
846+
u8 *sym;
847+
848+
sym = kmalloc(enclen + 1, GFP_NOFS);
849+
if (!sym)
850+
return -ENOMEM;
851+
852+
declen = ceph_base64_decode(encsym, enclen, sym);
853+
if (declen < 0) {
854+
pr_err("%s: can't decode symlink (%d). Content: %.*s\n",
855+
__func__, declen, enclen, encsym);
856+
kfree(sym);
857+
return -EIO;
858+
}
859+
sym[declen + 1] = '\0';
860+
*decsym = sym;
861+
return declen;
862+
}
863+
#else
864+
static int decode_encrypted_symlink(const char *encsym, int symlen, u8 **decsym)
865+
{
866+
return -EOPNOTSUPP;
867+
}
868+
#endif
869+
840870
/*
841871
* Populate an inode based on info from mds. May be called on new or
842872
* existing inodes.
@@ -1071,34 +1101,60 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
10711101
inode->i_fop = &ceph_file_fops;
10721102
break;
10731103
case S_IFLNK:
1074-
inode->i_op = &ceph_symlink_iops;
10751104
if (!ci->i_symlink) {
10761105
u32 symlen = iinfo->symlink_len;
10771106
char *sym;
10781107

10791108
spin_unlock(&ci->i_ceph_lock);
10801109

1081-
if (symlen != i_size_read(inode)) {
1082-
pr_err("%s %llx.%llx BAD symlink "
1083-
"size %lld\n", __func__,
1084-
ceph_vinop(inode),
1085-
i_size_read(inode));
1110+
if (IS_ENCRYPTED(inode)) {
1111+
if (symlen != i_size_read(inode))
1112+
pr_err("%s %llx.%llx BAD symlink size %lld\n",
1113+
__func__, ceph_vinop(inode),
1114+
i_size_read(inode));
1115+
1116+
err = decode_encrypted_symlink(iinfo->symlink,
1117+
symlen, (u8 **)&sym);
1118+
if (err < 0) {
1119+
pr_err("%s decoding encrypted symlink failed: %d\n",
1120+
__func__, err);
1121+
goto out;
1122+
}
1123+
symlen = err;
10861124
i_size_write(inode, symlen);
10871125
inode->i_blocks = calc_inode_blocks(symlen);
1088-
}
1126+
} else {
1127+
if (symlen != i_size_read(inode)) {
1128+
pr_err("%s %llx.%llx BAD symlink size %lld\n",
1129+
__func__, ceph_vinop(inode),
1130+
i_size_read(inode));
1131+
i_size_write(inode, symlen);
1132+
inode->i_blocks = calc_inode_blocks(symlen);
1133+
}
10891134

1090-
err = -ENOMEM;
1091-
sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS);
1092-
if (!sym)
1093-
goto out;
1135+
err = -ENOMEM;
1136+
sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS);
1137+
if (!sym)
1138+
goto out;
1139+
}
10941140

10951141
spin_lock(&ci->i_ceph_lock);
10961142
if (!ci->i_symlink)
10971143
ci->i_symlink = sym;
10981144
else
10991145
kfree(sym); /* lost a race */
11001146
}
1101-
inode->i_link = ci->i_symlink;
1147+
1148+
if (IS_ENCRYPTED(inode)) {
1149+
/*
1150+
* Encrypted symlinks need to be decrypted before we can
1151+
* cache their targets in i_link. Don't touch it here.
1152+
*/
1153+
inode->i_op = &ceph_encrypted_symlink_iops;
1154+
} else {
1155+
inode->i_link = ci->i_symlink;
1156+
inode->i_op = &ceph_symlink_iops;
1157+
}
11021158
break;
11031159
case S_IFDIR:
11041160
inode->i_op = &ceph_dir_iops;
@@ -2126,6 +2182,32 @@ static void ceph_inode_work(struct work_struct *work)
21262182
iput(inode);
21272183
}
21282184

2185+
static const char *ceph_encrypted_get_link(struct dentry *dentry,
2186+
struct inode *inode,
2187+
struct delayed_call *done)
2188+
{
2189+
struct ceph_inode_info *ci = ceph_inode(inode);
2190+
2191+
if (!dentry)
2192+
return ERR_PTR(-ECHILD);
2193+
2194+
return fscrypt_get_symlink(inode, ci->i_symlink, i_size_read(inode),
2195+
done);
2196+
}
2197+
2198+
static int ceph_encrypted_symlink_getattr(struct mnt_idmap *idmap,
2199+
const struct path *path,
2200+
struct kstat *stat, u32 request_mask,
2201+
unsigned int query_flags)
2202+
{
2203+
int ret;
2204+
2205+
ret = ceph_getattr(idmap, path, stat, request_mask, query_flags);
2206+
if (ret)
2207+
return ret;
2208+
return fscrypt_symlink_getattr(path, stat);
2209+
}
2210+
21292211
/*
21302212
* symlinks
21312213
*/
@@ -2136,6 +2218,13 @@ static const struct inode_operations ceph_symlink_iops = {
21362218
.listxattr = ceph_listxattr,
21372219
};
21382220

2221+
static const struct inode_operations ceph_encrypted_symlink_iops = {
2222+
.get_link = ceph_encrypted_get_link,
2223+
.setattr = ceph_setattr,
2224+
.getattr = ceph_encrypted_symlink_getattr,
2225+
.listxattr = ceph_listxattr,
2226+
};
2227+
21392228
int __ceph_setattr(struct inode *inode, struct iattr *attr,
21402229
struct ceph_iattr *cia)
21412230
{

0 commit comments

Comments
 (0)