Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Jeff Layton and committed by
Ilya Dryomov
79f2f6ad af9ffa6d

+150 -17
+49 -5
fs/ceph/dir.c
··· 948 948 return ceph_mknod(idmap, dir, dentry, mode, 0); 949 949 } 950 950 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 + 951 988 static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir, 952 989 struct dentry *dentry, const char *dest) 953 990 { ··· 1020 983 goto out_req; 1021 984 } 1022 985 1023 - req->r_path2 = kstrdup(dest, GFP_KERNEL); 1024 - if (!req->r_path2) { 1025 - err = -ENOMEM; 1026 - goto out_req; 1027 - } 1028 986 req->r_parent = dir; 1029 987 ihold(dir); 988 + 989 + if (IS_ENCRYPTED(req->r_new_inode)) { 990 + err = prep_encrypted_symlink_target(req, dest); 991 + if (err) 992 + goto out_req; 993 + } else { 994 + req->r_path2 = kstrdup(dest, GFP_KERNEL); 995 + if (!req->r_path2) { 996 + err = -ENOMEM; 997 + goto out_req; 998 + } 999 + } 1030 1000 1031 1001 set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 1032 1002 req->r_dentry = dget(dentry);
+101 -12
fs/ceph/inode.c
··· 35 35 */ 36 36 37 37 static const struct inode_operations ceph_symlink_iops; 38 + static const struct inode_operations ceph_encrypted_symlink_iops; 38 39 39 40 static void ceph_inode_work(struct work_struct *work); 40 41 ··· 641 640 #ifdef CONFIG_FS_ENCRYPTION 642 641 kfree(ci->fscrypt_auth); 643 642 #endif 643 + fscrypt_free_inode(inode); 644 644 kmem_cache_free(ceph_inode_cachep, ci); 645 645 } 646 646 ··· 838 836 dout("%p mds time_warp_seq %llu < %u\n", 839 837 inode, time_warp_seq, ci->i_time_warp_seq); 840 838 } 839 + 840 + #if IS_ENABLED(CONFIG_FS_ENCRYPTION) 841 + static int decode_encrypted_symlink(const char *encsym, int enclen, u8 **decsym) 842 + { 843 + int declen; 844 + u8 *sym; 845 + 846 + sym = kmalloc(enclen + 1, GFP_NOFS); 847 + if (!sym) 848 + return -ENOMEM; 849 + 850 + declen = ceph_base64_decode(encsym, enclen, sym); 851 + if (declen < 0) { 852 + pr_err("%s: can't decode symlink (%d). Content: %.*s\n", 853 + __func__, declen, enclen, encsym); 854 + kfree(sym); 855 + return -EIO; 856 + } 857 + sym[declen + 1] = '\0'; 858 + *decsym = sym; 859 + return declen; 860 + } 861 + #else 862 + static int decode_encrypted_symlink(const char *encsym, int symlen, u8 **decsym) 863 + { 864 + return -EOPNOTSUPP; 865 + } 866 + #endif 841 867 842 868 /* 843 869 * Populate an inode based on info from mds. May be called on new or ··· 1101 1071 inode->i_fop = &ceph_file_fops; 1102 1072 break; 1103 1073 case S_IFLNK: 1104 - inode->i_op = &ceph_symlink_iops; 1105 1074 if (!ci->i_symlink) { 1106 1075 u32 symlen = iinfo->symlink_len; 1107 1076 char *sym; 1108 1077 1109 1078 spin_unlock(&ci->i_ceph_lock); 1110 1079 1111 - if (symlen != i_size_read(inode)) { 1112 - pr_err("%s %llx.%llx BAD symlink " 1113 - "size %lld\n", __func__, 1114 - ceph_vinop(inode), 1115 - i_size_read(inode)); 1080 + if (IS_ENCRYPTED(inode)) { 1081 + if (symlen != i_size_read(inode)) 1082 + pr_err("%s %llx.%llx BAD symlink size %lld\n", 1083 + __func__, ceph_vinop(inode), 1084 + i_size_read(inode)); 1085 + 1086 + err = decode_encrypted_symlink(iinfo->symlink, 1087 + symlen, (u8 **)&sym); 1088 + if (err < 0) { 1089 + pr_err("%s decoding encrypted symlink failed: %d\n", 1090 + __func__, err); 1091 + goto out; 1092 + } 1093 + symlen = err; 1116 1094 i_size_write(inode, symlen); 1117 1095 inode->i_blocks = calc_inode_blocks(symlen); 1118 - } 1096 + } else { 1097 + if (symlen != i_size_read(inode)) { 1098 + pr_err("%s %llx.%llx BAD symlink size %lld\n", 1099 + __func__, ceph_vinop(inode), 1100 + i_size_read(inode)); 1101 + i_size_write(inode, symlen); 1102 + inode->i_blocks = calc_inode_blocks(symlen); 1103 + } 1119 1104 1120 - err = -ENOMEM; 1121 - sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS); 1122 - if (!sym) 1123 - goto out; 1105 + err = -ENOMEM; 1106 + sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS); 1107 + if (!sym) 1108 + goto out; 1109 + } 1124 1110 1125 1111 spin_lock(&ci->i_ceph_lock); 1126 1112 if (!ci->i_symlink) ··· 1144 1098 else 1145 1099 kfree(sym); /* lost a race */ 1146 1100 } 1147 - inode->i_link = ci->i_symlink; 1101 + 1102 + if (IS_ENCRYPTED(inode)) { 1103 + /* 1104 + * Encrypted symlinks need to be decrypted before we can 1105 + * cache their targets in i_link. Don't touch it here. 1106 + */ 1107 + inode->i_op = &ceph_encrypted_symlink_iops; 1108 + } else { 1109 + inode->i_link = ci->i_symlink; 1110 + inode->i_op = &ceph_symlink_iops; 1111 + } 1148 1112 break; 1149 1113 case S_IFDIR: 1150 1114 inode->i_op = &ceph_dir_iops; ··· 2182 2126 iput(inode); 2183 2127 } 2184 2128 2129 + static const char *ceph_encrypted_get_link(struct dentry *dentry, 2130 + struct inode *inode, 2131 + struct delayed_call *done) 2132 + { 2133 + struct ceph_inode_info *ci = ceph_inode(inode); 2134 + 2135 + if (!dentry) 2136 + return ERR_PTR(-ECHILD); 2137 + 2138 + return fscrypt_get_symlink(inode, ci->i_symlink, i_size_read(inode), 2139 + done); 2140 + } 2141 + 2142 + static int ceph_encrypted_symlink_getattr(struct mnt_idmap *idmap, 2143 + const struct path *path, 2144 + struct kstat *stat, u32 request_mask, 2145 + unsigned int query_flags) 2146 + { 2147 + int ret; 2148 + 2149 + ret = ceph_getattr(idmap, path, stat, request_mask, query_flags); 2150 + if (ret) 2151 + return ret; 2152 + return fscrypt_symlink_getattr(path, stat); 2153 + } 2154 + 2185 2155 /* 2186 2156 * symlinks 2187 2157 */ ··· 2215 2133 .get_link = simple_get_link, 2216 2134 .setattr = ceph_setattr, 2217 2135 .getattr = ceph_getattr, 2136 + .listxattr = ceph_listxattr, 2137 + }; 2138 + 2139 + static const struct inode_operations ceph_encrypted_symlink_iops = { 2140 + .get_link = ceph_encrypted_get_link, 2141 + .setattr = ceph_setattr, 2142 + .getattr = ceph_encrypted_symlink_getattr, 2218 2143 .listxattr = ceph_listxattr, 2219 2144 }; 2220 2145