162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * The base64 encode/decode code was copied from fscrypt: 462306a36Sopenharmony_ci * Copyright (C) 2015, Google, Inc. 562306a36Sopenharmony_ci * Copyright (C) 2015, Motorola Mobility 662306a36Sopenharmony_ci * Written by Uday Savagaonkar, 2014. 762306a36Sopenharmony_ci * Modified by Jaegeuk Kim, 2015. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 1062306a36Sopenharmony_ci#include <linux/xattr.h> 1162306a36Sopenharmony_ci#include <linux/fscrypt.h> 1262306a36Sopenharmony_ci#include <linux/ceph/striper.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "super.h" 1562306a36Sopenharmony_ci#include "mds_client.h" 1662306a36Sopenharmony_ci#include "crypto.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * The base64url encoding used by fscrypt includes the '_' character, which may 2062306a36Sopenharmony_ci * cause problems in snapshot names (which can not start with '_'). Thus, we 2162306a36Sopenharmony_ci * used the base64 encoding defined for IMAP mailbox names (RFC 3501) instead, 2262306a36Sopenharmony_ci * which replaces '-' and '_' by '+' and ','. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic const char base64_table[65] = 2562306a36Sopenharmony_ci "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciint ceph_base64_encode(const u8 *src, int srclen, char *dst) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci u32 ac = 0; 3062306a36Sopenharmony_ci int bits = 0; 3162306a36Sopenharmony_ci int i; 3262306a36Sopenharmony_ci char *cp = dst; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (i = 0; i < srclen; i++) { 3562306a36Sopenharmony_ci ac = (ac << 8) | src[i]; 3662306a36Sopenharmony_ci bits += 8; 3762306a36Sopenharmony_ci do { 3862306a36Sopenharmony_ci bits -= 6; 3962306a36Sopenharmony_ci *cp++ = base64_table[(ac >> bits) & 0x3f]; 4062306a36Sopenharmony_ci } while (bits >= 6); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci if (bits) 4362306a36Sopenharmony_ci *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; 4462306a36Sopenharmony_ci return cp - dst; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint ceph_base64_decode(const char *src, int srclen, u8 *dst) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci u32 ac = 0; 5062306a36Sopenharmony_ci int bits = 0; 5162306a36Sopenharmony_ci int i; 5262306a36Sopenharmony_ci u8 *bp = dst; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < srclen; i++) { 5562306a36Sopenharmony_ci const char *p = strchr(base64_table, src[i]); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (p == NULL || src[i] == 0) 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci ac = (ac << 6) | (p - base64_table); 6062306a36Sopenharmony_ci bits += 6; 6162306a36Sopenharmony_ci if (bits >= 8) { 6262306a36Sopenharmony_ci bits -= 8; 6362306a36Sopenharmony_ci *bp++ = (u8)(ac >> bits); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci if (ac & ((1 << bits) - 1)) 6762306a36Sopenharmony_ci return -1; 6862306a36Sopenharmony_ci return bp - dst; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 7462306a36Sopenharmony_ci struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth; 7562306a36Sopenharmony_ci u32 ctxlen; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Non existent or too short? */ 7862306a36Sopenharmony_ci if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1))) 7962306a36Sopenharmony_ci return -ENOBUFS; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Some format we don't recognize? */ 8262306a36Sopenharmony_ci if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION) 8362306a36Sopenharmony_ci return -ENOBUFS; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ctxlen = le32_to_cpu(cfa->cfa_blob_len); 8662306a36Sopenharmony_ci if (len < ctxlen) 8762306a36Sopenharmony_ci return -ERANGE; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci memcpy(ctx, cfa->cfa_blob, ctxlen); 9062306a36Sopenharmony_ci return ctxlen; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int ceph_crypt_set_context(struct inode *inode, const void *ctx, 9462306a36Sopenharmony_ci size_t len, void *fs_data) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int ret; 9762306a36Sopenharmony_ci struct iattr attr = { }; 9862306a36Sopenharmony_ci struct ceph_iattr cia = { }; 9962306a36Sopenharmony_ci struct ceph_fscrypt_auth *cfa; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci WARN_ON_ONCE(fs_data); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cfa = kzalloc(sizeof(*cfa), GFP_KERNEL); 10762306a36Sopenharmony_ci if (!cfa) 10862306a36Sopenharmony_ci return -ENOMEM; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); 11162306a36Sopenharmony_ci cfa->cfa_blob_len = cpu_to_le32(len); 11262306a36Sopenharmony_ci memcpy(cfa->cfa_blob, ctx, len); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci cia.fscrypt_auth = cfa; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ret = __ceph_setattr(inode, &attr, &cia); 11762306a36Sopenharmony_ci if (ret == 0) 11862306a36Sopenharmony_ci inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED); 11962306a36Sopenharmony_ci kfree(cia.fscrypt_auth); 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic bool ceph_crypt_empty_dir(struct inode *inode) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return ci->i_rsubdirs + ci->i_rfiles == 1; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return ceph_sb_to_client(sb)->fsc_dummy_enc_policy.policy; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct fscrypt_operations ceph_fscrypt_ops = { 13662306a36Sopenharmony_ci .get_context = ceph_crypt_get_context, 13762306a36Sopenharmony_ci .set_context = ceph_crypt_set_context, 13862306a36Sopenharmony_ci .get_dummy_policy = ceph_get_dummy_policy, 13962306a36Sopenharmony_ci .empty_dir = ceph_crypt_empty_dir, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid ceph_fscrypt_set_ops(struct super_block *sb) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci fscrypt_set_ops(sb, &ceph_fscrypt_ops); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_civoid ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci fscrypt_free_dummy_policy(&fsc->fsc_dummy_enc_policy); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciint ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, 15362306a36Sopenharmony_ci struct ceph_acl_sec_ctx *as) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int ret, ctxsize; 15662306a36Sopenharmony_ci bool encrypted = false; 15762306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = fscrypt_prepare_new_inode(dir, inode, &encrypted); 16062306a36Sopenharmony_ci if (ret) 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci if (!encrypted) 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!as->fscrypt_auth) 16762306a36Sopenharmony_ci return -ENOMEM; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, 17062306a36Sopenharmony_ci inode); 17162306a36Sopenharmony_ci if (ctxsize < 0) 17262306a36Sopenharmony_ci return ctxsize; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); 17562306a36Sopenharmony_ci as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci WARN_ON_ONCE(ci->fscrypt_auth); 17862306a36Sopenharmony_ci kfree(ci->fscrypt_auth); 17962306a36Sopenharmony_ci ci->fscrypt_auth_len = ceph_fscrypt_auth_len(as->fscrypt_auth); 18062306a36Sopenharmony_ci ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, 18162306a36Sopenharmony_ci GFP_KERNEL); 18262306a36Sopenharmony_ci if (!ci->fscrypt_auth) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci inode->i_flags |= S_ENCRYPTED; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, 19162306a36Sopenharmony_ci struct ceph_acl_sec_ctx *as) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci swap(req->r_fscrypt_auth, as->fscrypt_auth); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * User-created snapshots can't start with '_'. Snapshots that start with this 19862306a36Sopenharmony_ci * character are special (hint: there aren't real snapshots) and use the 19962306a36Sopenharmony_ci * following format: 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * _<SNAPSHOT-NAME>_<INODE-NUMBER> 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * where: 20462306a36Sopenharmony_ci * - <SNAPSHOT-NAME> - the real snapshot name that may need to be decrypted, 20562306a36Sopenharmony_ci * - <INODE-NUMBER> - the inode number (in decimal) for the actual snapshot 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * This function parses these snapshot names and returns the inode 20862306a36Sopenharmony_ci * <INODE-NUMBER>. 'name_len' will also bet set with the <SNAPSHOT-NAME> 20962306a36Sopenharmony_ci * length. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic struct inode *parse_longname(const struct inode *parent, 21262306a36Sopenharmony_ci const char *name, int *name_len) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct inode *dir = NULL; 21562306a36Sopenharmony_ci struct ceph_vino vino = { .snap = CEPH_NOSNAP }; 21662306a36Sopenharmony_ci char *inode_number; 21762306a36Sopenharmony_ci char *name_end; 21862306a36Sopenharmony_ci int orig_len = *name_len; 21962306a36Sopenharmony_ci int ret = -EIO; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Skip initial '_' */ 22262306a36Sopenharmony_ci name++; 22362306a36Sopenharmony_ci name_end = strrchr(name, '_'); 22462306a36Sopenharmony_ci if (!name_end) { 22562306a36Sopenharmony_ci dout("Failed to parse long snapshot name: %s\n", name); 22662306a36Sopenharmony_ci return ERR_PTR(-EIO); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci *name_len = (name_end - name); 22962306a36Sopenharmony_ci if (*name_len <= 0) { 23062306a36Sopenharmony_ci pr_err("Failed to parse long snapshot name\n"); 23162306a36Sopenharmony_ci return ERR_PTR(-EIO); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Get the inode number */ 23562306a36Sopenharmony_ci inode_number = kmemdup_nul(name_end + 1, 23662306a36Sopenharmony_ci orig_len - *name_len - 2, 23762306a36Sopenharmony_ci GFP_KERNEL); 23862306a36Sopenharmony_ci if (!inode_number) 23962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24062306a36Sopenharmony_ci ret = kstrtou64(inode_number, 10, &vino.ino); 24162306a36Sopenharmony_ci if (ret) { 24262306a36Sopenharmony_ci dout("Failed to parse inode number: %s\n", name); 24362306a36Sopenharmony_ci dir = ERR_PTR(ret); 24462306a36Sopenharmony_ci goto out; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* And finally the inode */ 24862306a36Sopenharmony_ci dir = ceph_find_inode(parent->i_sb, vino); 24962306a36Sopenharmony_ci if (!dir) { 25062306a36Sopenharmony_ci /* This can happen if we're not mounting cephfs on the root */ 25162306a36Sopenharmony_ci dir = ceph_get_inode(parent->i_sb, vino, NULL); 25262306a36Sopenharmony_ci if (IS_ERR(dir)) 25362306a36Sopenharmony_ci dout("Can't find inode %s (%s)\n", inode_number, name); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciout: 25762306a36Sopenharmony_ci kfree(inode_number); 25862306a36Sopenharmony_ci return dir; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciint ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, 26262306a36Sopenharmony_ci char *buf) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct inode *dir = parent; 26562306a36Sopenharmony_ci struct qstr iname; 26662306a36Sopenharmony_ci u32 len; 26762306a36Sopenharmony_ci int name_len; 26862306a36Sopenharmony_ci int elen; 26962306a36Sopenharmony_ci int ret; 27062306a36Sopenharmony_ci u8 *cryptbuf = NULL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci iname.name = d_name->name; 27362306a36Sopenharmony_ci name_len = d_name->len; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Handle the special case of snapshot names that start with '_' */ 27662306a36Sopenharmony_ci if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && 27762306a36Sopenharmony_ci (iname.name[0] == '_')) { 27862306a36Sopenharmony_ci dir = parse_longname(parent, iname.name, &name_len); 27962306a36Sopenharmony_ci if (IS_ERR(dir)) 28062306a36Sopenharmony_ci return PTR_ERR(dir); 28162306a36Sopenharmony_ci iname.name++; /* skip initial '_' */ 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci iname.len = name_len; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!fscrypt_has_encryption_key(dir)) { 28662306a36Sopenharmony_ci memcpy(buf, d_name->name, d_name->len); 28762306a36Sopenharmony_ci elen = d_name->len; 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Convert cleartext d_name to ciphertext. If result is longer than 29362306a36Sopenharmony_ci * CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * See: fscrypt_setup_filename 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) { 29862306a36Sopenharmony_ci elen = -ENAMETOOLONG; 29962306a36Sopenharmony_ci goto out; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Allocate a buffer appropriate to hold the result */ 30362306a36Sopenharmony_ci cryptbuf = kmalloc(len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len, 30462306a36Sopenharmony_ci GFP_KERNEL); 30562306a36Sopenharmony_ci if (!cryptbuf) { 30662306a36Sopenharmony_ci elen = -ENOMEM; 30762306a36Sopenharmony_ci goto out; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len); 31162306a36Sopenharmony_ci if (ret) { 31262306a36Sopenharmony_ci elen = ret; 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* hash the end if the name is long enough */ 31762306a36Sopenharmony_ci if (len > CEPH_NOHASH_NAME_MAX) { 31862306a36Sopenharmony_ci u8 hash[SHA256_DIGEST_SIZE]; 31962306a36Sopenharmony_ci u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * hash the extra bytes and overwrite crypttext beyond that 32362306a36Sopenharmony_ci * point with it 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash); 32662306a36Sopenharmony_ci memcpy(extra, hash, SHA256_DIGEST_SIZE); 32762306a36Sopenharmony_ci len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* base64 encode the encrypted name */ 33162306a36Sopenharmony_ci elen = ceph_base64_encode(cryptbuf, len, buf); 33262306a36Sopenharmony_ci dout("base64-encoded ciphertext name = %.*s\n", elen, buf); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ 33562306a36Sopenharmony_ci WARN_ON(elen > 240); 33662306a36Sopenharmony_ci if ((elen > 0) && (dir != parent)) { 33762306a36Sopenharmony_ci char tmp_buf[NAME_MAX]; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", 34062306a36Sopenharmony_ci elen, buf, dir->i_ino); 34162306a36Sopenharmony_ci memcpy(buf, tmp_buf, elen); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciout: 34562306a36Sopenharmony_ci kfree(cryptbuf); 34662306a36Sopenharmony_ci if (dir != parent) { 34762306a36Sopenharmony_ci if ((dir->i_state & I_NEW)) 34862306a36Sopenharmony_ci discard_new_inode(dir); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci iput(dir); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci return elen; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciint ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, 35662306a36Sopenharmony_ci char *buf) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * ceph_fname_to_usr - convert a filename for userland presentation 36562306a36Sopenharmony_ci * @fname: ceph_fname to be converted 36662306a36Sopenharmony_ci * @tname: temporary name buffer to use for conversion (may be NULL) 36762306a36Sopenharmony_ci * @oname: where converted name should be placed 36862306a36Sopenharmony_ci * @is_nokey: set to true if key wasn't available during conversion (may be NULL) 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Given a filename (usually from the MDS), format it for presentation to 37162306a36Sopenharmony_ci * userland. If @parent is not encrypted, just pass it back as-is. 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci * Otherwise, base64 decode the string, and then ask fscrypt to format it 37462306a36Sopenharmony_ci * for userland presentation. 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * Returns 0 on success or negative error code on error. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ciint ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, 37962306a36Sopenharmony_ci struct fscrypt_str *oname, bool *is_nokey) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct inode *dir = fname->dir; 38262306a36Sopenharmony_ci struct fscrypt_str _tname = FSTR_INIT(NULL, 0); 38362306a36Sopenharmony_ci struct fscrypt_str iname; 38462306a36Sopenharmony_ci char *name = fname->name; 38562306a36Sopenharmony_ci int name_len = fname->name_len; 38662306a36Sopenharmony_ci int ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Sanity check that the resulting name will fit in the buffer */ 38962306a36Sopenharmony_ci if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) 39062306a36Sopenharmony_ci return -EIO; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Handle the special case of snapshot names that start with '_' */ 39362306a36Sopenharmony_ci if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && 39462306a36Sopenharmony_ci (name[0] == '_')) { 39562306a36Sopenharmony_ci dir = parse_longname(dir, name, &name_len); 39662306a36Sopenharmony_ci if (IS_ERR(dir)) 39762306a36Sopenharmony_ci return PTR_ERR(dir); 39862306a36Sopenharmony_ci name++; /* skip initial '_' */ 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!IS_ENCRYPTED(dir)) { 40262306a36Sopenharmony_ci oname->name = fname->name; 40362306a36Sopenharmony_ci oname->len = fname->name_len; 40462306a36Sopenharmony_ci ret = 0; 40562306a36Sopenharmony_ci goto out_inode; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ret = ceph_fscrypt_prepare_readdir(dir); 40962306a36Sopenharmony_ci if (ret) 41062306a36Sopenharmony_ci goto out_inode; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Use the raw dentry name as sent by the MDS instead of 41462306a36Sopenharmony_ci * generating a nokey name via fscrypt. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci if (!fscrypt_has_encryption_key(dir)) { 41762306a36Sopenharmony_ci if (fname->no_copy) 41862306a36Sopenharmony_ci oname->name = fname->name; 41962306a36Sopenharmony_ci else 42062306a36Sopenharmony_ci memcpy(oname->name, fname->name, fname->name_len); 42162306a36Sopenharmony_ci oname->len = fname->name_len; 42262306a36Sopenharmony_ci if (is_nokey) 42362306a36Sopenharmony_ci *is_nokey = true; 42462306a36Sopenharmony_ci ret = 0; 42562306a36Sopenharmony_ci goto out_inode; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (fname->ctext_len == 0) { 42962306a36Sopenharmony_ci int declen; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (!tname) { 43262306a36Sopenharmony_ci ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); 43362306a36Sopenharmony_ci if (ret) 43462306a36Sopenharmony_ci goto out_inode; 43562306a36Sopenharmony_ci tname = &_tname; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci declen = ceph_base64_decode(name, name_len, tname->name); 43962306a36Sopenharmony_ci if (declen <= 0) { 44062306a36Sopenharmony_ci ret = -EIO; 44162306a36Sopenharmony_ci goto out; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci iname.name = tname->name; 44462306a36Sopenharmony_ci iname.len = declen; 44562306a36Sopenharmony_ci } else { 44662306a36Sopenharmony_ci iname.name = fname->ctext; 44762306a36Sopenharmony_ci iname.len = fname->ctext_len; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname); 45162306a36Sopenharmony_ci if (!ret && (dir != fname->dir)) { 45262306a36Sopenharmony_ci char tmp_buf[CEPH_BASE64_CHARS(NAME_MAX)]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", 45562306a36Sopenharmony_ci oname->len, oname->name, dir->i_ino); 45662306a36Sopenharmony_ci memcpy(oname->name, tmp_buf, name_len); 45762306a36Sopenharmony_ci oname->len = name_len; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciout: 46162306a36Sopenharmony_ci fscrypt_fname_free_buffer(&_tname); 46262306a36Sopenharmony_ciout_inode: 46362306a36Sopenharmony_ci if (dir != fname->dir) { 46462306a36Sopenharmony_ci if ((dir->i_state & I_NEW)) 46562306a36Sopenharmony_ci discard_new_inode(dir); 46662306a36Sopenharmony_ci else 46762306a36Sopenharmony_ci iput(dir); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/** 47362306a36Sopenharmony_ci * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper 47462306a36Sopenharmony_ci * @dir: directory inode for readdir prep 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as 47762306a36Sopenharmony_ci * non-complete if this call results in having the directory unlocked. 47862306a36Sopenharmony_ci * 47962306a36Sopenharmony_ci * Returns: 48062306a36Sopenharmony_ci * 1 - if directory was locked and key is now loaded (i.e. dir is unlocked) 48162306a36Sopenharmony_ci * 0 - if directory is still locked 48262306a36Sopenharmony_ci * < 0 - if __fscrypt_prepare_readdir() fails 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ciint ceph_fscrypt_prepare_readdir(struct inode *dir) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci bool had_key = fscrypt_has_encryption_key(dir); 48762306a36Sopenharmony_ci int err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!IS_ENCRYPTED(dir)) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci err = __fscrypt_prepare_readdir(dir); 49362306a36Sopenharmony_ci if (err) 49462306a36Sopenharmony_ci return err; 49562306a36Sopenharmony_ci if (!had_key && fscrypt_has_encryption_key(dir)) { 49662306a36Sopenharmony_ci /* directory just got unlocked, mark it as not complete */ 49762306a36Sopenharmony_ci ceph_dir_clear_complete(dir); 49862306a36Sopenharmony_ci return 1; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ciint ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, 50462306a36Sopenharmony_ci struct page *page, unsigned int len, 50562306a36Sopenharmony_ci unsigned int offs, u64 lblk_num) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); 50862306a36Sopenharmony_ci return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciint ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, 51262306a36Sopenharmony_ci struct page *page, unsigned int len, 51362306a36Sopenharmony_ci unsigned int offs, u64 lblk_num, 51462306a36Sopenharmony_ci gfp_t gfp_flags) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); 51762306a36Sopenharmony_ci return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num, 51862306a36Sopenharmony_ci gfp_flags); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * ceph_fscrypt_decrypt_pages - decrypt an array of pages 52362306a36Sopenharmony_ci * @inode: pointer to inode associated with these pages 52462306a36Sopenharmony_ci * @page: pointer to page array 52562306a36Sopenharmony_ci * @off: offset into the file that the read data starts 52662306a36Sopenharmony_ci * @len: max length to decrypt 52762306a36Sopenharmony_ci * 52862306a36Sopenharmony_ci * Decrypt an array of fscrypt'ed pages and return the amount of 52962306a36Sopenharmony_ci * data decrypted. Any data in the page prior to the start of the 53062306a36Sopenharmony_ci * first complete block in the read is ignored. Any incomplete 53162306a36Sopenharmony_ci * crypto blocks at the end of the array are ignored (and should 53262306a36Sopenharmony_ci * probably be zeroed by the caller). 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * Returns the length of the decrypted data or a negative errno. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ciint ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page, 53762306a36Sopenharmony_ci u64 off, int len) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci int i, num_blocks; 54062306a36Sopenharmony_ci u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; 54162306a36Sopenharmony_ci int ret = 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * We can't deal with partial blocks on an encrypted file, so mask off 54562306a36Sopenharmony_ci * the last bit. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Decrypt each block */ 55062306a36Sopenharmony_ci for (i = 0; i < num_blocks; ++i) { 55162306a36Sopenharmony_ci int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; 55262306a36Sopenharmony_ci int pgidx = blkoff >> PAGE_SHIFT; 55362306a36Sopenharmony_ci unsigned int pgoffs = offset_in_page(blkoff); 55462306a36Sopenharmony_ci int fret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx], 55762306a36Sopenharmony_ci CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, 55862306a36Sopenharmony_ci baseblk + i); 55962306a36Sopenharmony_ci if (fret < 0) { 56062306a36Sopenharmony_ci if (ret == 0) 56162306a36Sopenharmony_ci ret = fret; 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci ret += CEPH_FSCRYPT_BLOCK_SIZE; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/** 57062306a36Sopenharmony_ci * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer 57162306a36Sopenharmony_ci * @inode: inode associated with pages being decrypted 57262306a36Sopenharmony_ci * @page: pointer to page array 57362306a36Sopenharmony_ci * @off: offset into the file that the data in page[0] starts 57462306a36Sopenharmony_ci * @map: pointer to extent array 57562306a36Sopenharmony_ci * @ext_cnt: length of extent array 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci * Given an extent map and a page array, decrypt the received data in-place, 57862306a36Sopenharmony_ci * skipping holes. Returns the offset into buffer of end of last decrypted 57962306a36Sopenharmony_ci * block. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ciint ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page, 58262306a36Sopenharmony_ci u64 off, struct ceph_sparse_extent *map, 58362306a36Sopenharmony_ci u32 ext_cnt) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int i, ret = 0; 58662306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 58762306a36Sopenharmony_ci u64 objno, objoff; 58862306a36Sopenharmony_ci u32 xlen; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Nothing to do for empty array */ 59162306a36Sopenharmony_ci if (ext_cnt == 0) { 59262306a36Sopenharmony_ci dout("%s: empty array, ret 0\n", __func__); 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ceph_calc_file_object_mapping(&ci->i_layout, off, map[0].len, 59762306a36Sopenharmony_ci &objno, &objoff, &xlen); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci for (i = 0; i < ext_cnt; ++i) { 60062306a36Sopenharmony_ci struct ceph_sparse_extent *ext = &map[i]; 60162306a36Sopenharmony_ci int pgsoff = ext->off - objoff; 60262306a36Sopenharmony_ci int pgidx = pgsoff >> PAGE_SHIFT; 60362306a36Sopenharmony_ci int fret; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) { 60662306a36Sopenharmony_ci pr_warn("%s: bad encrypted sparse extent idx %d off %llx len %llx\n", 60762306a36Sopenharmony_ci __func__, i, ext->off, ext->len); 60862306a36Sopenharmony_ci return -EIO; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx], 61162306a36Sopenharmony_ci off + pgsoff, ext->len); 61262306a36Sopenharmony_ci dout("%s: [%d] 0x%llx~0x%llx fret %d\n", __func__, i, 61362306a36Sopenharmony_ci ext->off, ext->len, fret); 61462306a36Sopenharmony_ci if (fret < 0) { 61562306a36Sopenharmony_ci if (ret == 0) 61662306a36Sopenharmony_ci ret = fret; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci ret = pgsoff + fret; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci dout("%s: ret %d\n", __func__, ret); 62262306a36Sopenharmony_ci return ret; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/** 62662306a36Sopenharmony_ci * ceph_fscrypt_encrypt_pages - encrypt an array of pages 62762306a36Sopenharmony_ci * @inode: pointer to inode associated with these pages 62862306a36Sopenharmony_ci * @page: pointer to page array 62962306a36Sopenharmony_ci * @off: offset into the file that the data starts 63062306a36Sopenharmony_ci * @len: max length to encrypt 63162306a36Sopenharmony_ci * @gfp: gfp flags to use for allocation 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * Decrypt an array of cleartext pages and return the amount of 63462306a36Sopenharmony_ci * data encrypted. Any data in the page prior to the start of the 63562306a36Sopenharmony_ci * first complete block in the read is ignored. Any incomplete 63662306a36Sopenharmony_ci * crypto blocks at the end of the array are ignored. 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * Returns the length of the encrypted data or a negative errno. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ciint ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, 64162306a36Sopenharmony_ci int len, gfp_t gfp) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int i, num_blocks; 64462306a36Sopenharmony_ci u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; 64562306a36Sopenharmony_ci int ret = 0; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * We can't deal with partial blocks on an encrypted file, so mask off 64962306a36Sopenharmony_ci * the last bit. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Encrypt each block */ 65462306a36Sopenharmony_ci for (i = 0; i < num_blocks; ++i) { 65562306a36Sopenharmony_ci int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; 65662306a36Sopenharmony_ci int pgidx = blkoff >> PAGE_SHIFT; 65762306a36Sopenharmony_ci unsigned int pgoffs = offset_in_page(blkoff); 65862306a36Sopenharmony_ci int fret; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx], 66162306a36Sopenharmony_ci CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, 66262306a36Sopenharmony_ci baseblk + i, gfp); 66362306a36Sopenharmony_ci if (fret < 0) { 66462306a36Sopenharmony_ci if (ret == 0) 66562306a36Sopenharmony_ci ret = fret; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci ret += CEPH_FSCRYPT_BLOCK_SIZE; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci return ret; 67162306a36Sopenharmony_ci} 672