mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
ceph: encode encrypted name in ceph_mdsc_build_path and dentry release
Allow ceph_mdsc_build_path to encrypt and base64 encode the filename when the parent is encrypted and we're sending the path to the MDS. In a similar fashion, encode encrypted dentry names if including a dentry release in a request. In most cases, we just encrypt the filenames and base64 encode them, but when the name is longer than CEPH_NOHASH_NAME_MAX, we use a similar scheme to fscrypt proper, and hash the remaning bits with sha256. When doing this, we then send along the full crypttext of the name in the new alternate_name field of the MClientRequest. The MDS can then send that along in readdir responses and traces. [ idryomov: drop duplicate include reported by Abaci Robot ] 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>
This commit is contained in:
parent
64e86f632b
commit
3fd945a79e
|
|
@ -4663,6 +4663,18 @@ int ceph_encode_inode_release(void **p, struct inode *inode,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ceph_encode_dentry_release - encode a dentry release into an outgoing request
|
||||||
|
* @p: outgoing request buffer
|
||||||
|
* @dentry: dentry to release
|
||||||
|
* @dir: dir to release it from
|
||||||
|
* @mds: mds that we're speaking to
|
||||||
|
* @drop: caps being dropped
|
||||||
|
* @unless: unless we have these caps
|
||||||
|
*
|
||||||
|
* Encode a dentry release into an outgoing request buffer. Returns 1 if the
|
||||||
|
* thing was released, or a negative error code otherwise.
|
||||||
|
*/
|
||||||
int ceph_encode_dentry_release(void **p, struct dentry *dentry,
|
int ceph_encode_dentry_release(void **p, struct dentry *dentry,
|
||||||
struct inode *dir,
|
struct inode *dir,
|
||||||
int mds, int drop, int unless)
|
int mds, int drop, int unless)
|
||||||
|
|
@ -4695,13 +4707,25 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry,
|
||||||
if (ret && di->lease_session && di->lease_session->s_mds == mds) {
|
if (ret && di->lease_session && di->lease_session->s_mds == mds) {
|
||||||
dout("encode_dentry_release %p mds%d seq %d\n",
|
dout("encode_dentry_release %p mds%d seq %d\n",
|
||||||
dentry, mds, (int)di->lease_seq);
|
dentry, mds, (int)di->lease_seq);
|
||||||
|
rel->dname_seq = cpu_to_le32(di->lease_seq);
|
||||||
|
__ceph_mdsc_drop_dentry_lease(dentry);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
if (IS_ENCRYPTED(dir) && fscrypt_has_encryption_key(dir)) {
|
||||||
|
int ret2 = ceph_encode_encrypted_fname(dir, dentry, *p);
|
||||||
|
|
||||||
|
if (ret2 < 0)
|
||||||
|
return ret2;
|
||||||
|
|
||||||
|
rel->dname_len = cpu_to_le32(ret2);
|
||||||
|
*p += ret2;
|
||||||
|
} else {
|
||||||
rel->dname_len = cpu_to_le32(dentry->d_name.len);
|
rel->dname_len = cpu_to_le32(dentry->d_name.len);
|
||||||
memcpy(*p, dentry->d_name.name, dentry->d_name.len);
|
memcpy(*p, dentry->d_name.name, dentry->d_name.len);
|
||||||
*p += dentry->d_name.len;
|
*p += dentry->d_name.len;
|
||||||
rel->dname_seq = cpu_to_le32(di->lease_seq);
|
|
||||||
__ceph_mdsc_drop_dentry_lease(dentry);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,3 +191,56 @@ void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
||||||
{
|
{
|
||||||
swap(req->r_fscrypt_auth, as->fscrypt_auth);
|
swap(req->r_fscrypt_auth, as->fscrypt_auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ceph_encode_encrypted_fname(const struct inode *parent,
|
||||||
|
struct dentry *dentry, char *buf)
|
||||||
|
{
|
||||||
|
u32 len;
|
||||||
|
int elen;
|
||||||
|
int ret;
|
||||||
|
u8 *cryptbuf;
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert cleartext d_name to ciphertext. If result is longer than
|
||||||
|
* CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes
|
||||||
|
*
|
||||||
|
* See: fscrypt_setup_filename
|
||||||
|
*/
|
||||||
|
if (!fscrypt_fname_encrypted_size(parent, dentry->d_name.len, NAME_MAX,
|
||||||
|
&len))
|
||||||
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
|
/* Allocate a buffer appropriate to hold the result */
|
||||||
|
cryptbuf = kmalloc(len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!cryptbuf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = fscrypt_fname_encrypt(parent, &dentry->d_name, cryptbuf, len);
|
||||||
|
if (ret) {
|
||||||
|
kfree(cryptbuf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hash the end if the name is long enough */
|
||||||
|
if (len > CEPH_NOHASH_NAME_MAX) {
|
||||||
|
u8 hash[SHA256_DIGEST_SIZE];
|
||||||
|
u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hash the extra bytes and overwrite crypttext beyond that
|
||||||
|
* point with it
|
||||||
|
*/
|
||||||
|
sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
|
||||||
|
memcpy(extra, hash, SHA256_DIGEST_SIZE);
|
||||||
|
len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* base64 encode the encrypted name */
|
||||||
|
elen = ceph_base64_encode(cryptbuf, len, buf);
|
||||||
|
kfree(cryptbuf);
|
||||||
|
dout("base64-encoded ciphertext name = %.*s\n", elen, buf);
|
||||||
|
return elen;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#ifndef _CEPH_CRYPTO_H
|
#ifndef _CEPH_CRYPTO_H
|
||||||
#define _CEPH_CRYPTO_H
|
#define _CEPH_CRYPTO_H
|
||||||
|
|
||||||
|
#include <crypto/sha2.h>
|
||||||
#include <linux/fscrypt.h>
|
#include <linux/fscrypt.h>
|
||||||
|
|
||||||
struct ceph_fs_client;
|
struct ceph_fs_client;
|
||||||
|
|
@ -67,6 +68,8 @@ int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
|
||||||
struct ceph_acl_sec_ctx *as);
|
struct ceph_acl_sec_ctx *as);
|
||||||
void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
||||||
struct ceph_acl_sec_ctx *as);
|
struct ceph_acl_sec_ctx *as);
|
||||||
|
int ceph_encode_encrypted_fname(const struct inode *parent,
|
||||||
|
struct dentry *dentry, char *buf);
|
||||||
|
|
||||||
#else /* CONFIG_FS_ENCRYPTION */
|
#else /* CONFIG_FS_ENCRYPTION */
|
||||||
|
|
||||||
|
|
@ -91,6 +94,12 @@ static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
|
||||||
struct ceph_acl_sec_ctx *as_ctx)
|
struct ceph_acl_sec_ctx *as_ctx)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int ceph_encode_encrypted_fname(const struct inode *parent,
|
||||||
|
struct dentry *dentry, char *buf)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
#endif /* CONFIG_FS_ENCRYPTION */
|
#endif /* CONFIG_FS_ENCRYPTION */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2435,18 +2435,29 @@ static inline u64 __get_oldest_tid(struct ceph_mds_client *mdsc)
|
||||||
return mdsc->oldest_tid;
|
return mdsc->oldest_tid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Build a dentry's path. Allocate on heap; caller must kfree. Based
|
* ceph_mdsc_build_path - build a path string to a given dentry
|
||||||
* on build_path_from_dentry in fs/cifs/dir.c.
|
* @dentry: dentry to which path should be built
|
||||||
|
* @plen: returned length of string
|
||||||
|
* @pbase: returned base inode number
|
||||||
|
* @for_wire: is this path going to be sent to the MDS?
|
||||||
*
|
*
|
||||||
* If @stop_on_nosnap, generate path relative to the first non-snapped
|
* Build a string that represents the path to the dentry. This is mostly called
|
||||||
* inode.
|
* for two different purposes:
|
||||||
|
*
|
||||||
|
* 1) we need to build a path string to send to the MDS (for_wire == true)
|
||||||
|
* 2) we need a path string for local presentation (e.g. debugfs)
|
||||||
|
* (for_wire == false)
|
||||||
|
*
|
||||||
|
* The path is built in reverse, starting with the dentry. Walk back up toward
|
||||||
|
* the root, building the path until the first non-snapped inode is reached
|
||||||
|
* (for_wire) or the root inode is reached (!for_wire).
|
||||||
*
|
*
|
||||||
* Encode hidden .snap dirs as a double /, i.e.
|
* Encode hidden .snap dirs as a double /, i.e.
|
||||||
* foo/.snap/bar -> foo//bar
|
* foo/.snap/bar -> foo//bar
|
||||||
*/
|
*/
|
||||||
char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
|
char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
|
||||||
int stop_on_nosnap)
|
int for_wire)
|
||||||
{
|
{
|
||||||
struct dentry *cur;
|
struct dentry *cur;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
@ -2468,30 +2479,67 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
|
||||||
seq = read_seqbegin(&rename_lock);
|
seq = read_seqbegin(&rename_lock);
|
||||||
cur = dget(dentry);
|
cur = dget(dentry);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct dentry *temp;
|
struct dentry *parent;
|
||||||
|
|
||||||
spin_lock(&cur->d_lock);
|
spin_lock(&cur->d_lock);
|
||||||
inode = d_inode(cur);
|
inode = d_inode(cur);
|
||||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
||||||
dout("build_path path+%d: %p SNAPDIR\n",
|
dout("build_path path+%d: %p SNAPDIR\n",
|
||||||
pos, cur);
|
pos, cur);
|
||||||
} else if (stop_on_nosnap && inode && dentry != cur &&
|
spin_unlock(&cur->d_lock);
|
||||||
|
parent = dget_parent(cur);
|
||||||
|
} else if (for_wire && inode && dentry != cur &&
|
||||||
ceph_snap(inode) == CEPH_NOSNAP) {
|
ceph_snap(inode) == CEPH_NOSNAP) {
|
||||||
spin_unlock(&cur->d_lock);
|
spin_unlock(&cur->d_lock);
|
||||||
pos++; /* get rid of any prepended '/' */
|
pos++; /* get rid of any prepended '/' */
|
||||||
break;
|
break;
|
||||||
} else {
|
} else if (!for_wire || !IS_ENCRYPTED(d_inode(cur->d_parent))) {
|
||||||
pos -= cur->d_name.len;
|
pos -= cur->d_name.len;
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
spin_unlock(&cur->d_lock);
|
spin_unlock(&cur->d_lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memcpy(path + pos, cur->d_name.name, cur->d_name.len);
|
memcpy(path + pos, cur->d_name.name, cur->d_name.len);
|
||||||
|
spin_unlock(&cur->d_lock);
|
||||||
|
parent = dget_parent(cur);
|
||||||
|
} else {
|
||||||
|
int len, ret;
|
||||||
|
char buf[NAME_MAX];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Proactively copy name into buf, in case we need to
|
||||||
|
* present it as-is.
|
||||||
|
*/
|
||||||
|
memcpy(buf, cur->d_name.name, cur->d_name.len);
|
||||||
|
len = cur->d_name.len;
|
||||||
|
spin_unlock(&cur->d_lock);
|
||||||
|
parent = dget_parent(cur);
|
||||||
|
|
||||||
|
ret = __fscrypt_prepare_readdir(d_inode(parent));
|
||||||
|
if (ret < 0) {
|
||||||
|
dput(parent);
|
||||||
|
dput(cur);
|
||||||
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
temp = cur;
|
|
||||||
spin_unlock(&temp->d_lock);
|
if (fscrypt_has_encryption_key(d_inode(parent))) {
|
||||||
cur = dget_parent(temp);
|
len = ceph_encode_encrypted_fname(d_inode(parent),
|
||||||
dput(temp);
|
cur, buf);
|
||||||
|
if (len < 0) {
|
||||||
|
dput(parent);
|
||||||
|
dput(cur);
|
||||||
|
return ERR_PTR(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos -= len;
|
||||||
|
if (pos < 0) {
|
||||||
|
dput(parent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(path + pos, buf, len);
|
||||||
|
}
|
||||||
|
dput(cur);
|
||||||
|
cur = parent;
|
||||||
|
|
||||||
/* Are we at the root? */
|
/* Are we at the root? */
|
||||||
if (IS_ROOT(cur))
|
if (IS_ROOT(cur))
|
||||||
|
|
@ -2515,8 +2563,8 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
|
||||||
* A rename didn't occur, but somehow we didn't end up where
|
* A rename didn't occur, but somehow we didn't end up where
|
||||||
* we thought we would. Throw a warning and try again.
|
* we thought we would. Throw a warning and try again.
|
||||||
*/
|
*/
|
||||||
pr_warn("build_path did not end path lookup where "
|
pr_warn("build_path did not end path lookup where expected (pos = %d)\n",
|
||||||
"expected, pos is %d\n", pos);
|
pos);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2536,7 +2584,8 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (!dir)
|
if (!dir)
|
||||||
dir = d_inode_rcu(dentry->d_parent);
|
dir = d_inode_rcu(dentry->d_parent);
|
||||||
if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP) {
|
if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP &&
|
||||||
|
!IS_ENCRYPTED(dir)) {
|
||||||
*pino = ceph_ino(dir);
|
*pino = ceph_ino(dir);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
*ppath = dentry->d_name.name;
|
*ppath = dentry->d_name.name;
|
||||||
|
|
@ -2765,15 +2814,23 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
req->r_inode ? req->r_inode : d_inode(req->r_dentry),
|
req->r_inode ? req->r_inode : d_inode(req->r_dentry),
|
||||||
mds, req->r_inode_drop, req->r_inode_unless,
|
mds, req->r_inode_drop, req->r_inode_unless,
|
||||||
req->r_op == CEPH_MDS_OP_READDIR);
|
req->r_op == CEPH_MDS_OP_READDIR);
|
||||||
if (req->r_dentry_drop)
|
if (req->r_dentry_drop) {
|
||||||
releases += ceph_encode_dentry_release(&p, req->r_dentry,
|
ret = ceph_encode_dentry_release(&p, req->r_dentry,
|
||||||
req->r_parent, mds, req->r_dentry_drop,
|
req->r_parent, mds, req->r_dentry_drop,
|
||||||
req->r_dentry_unless);
|
req->r_dentry_unless);
|
||||||
if (req->r_old_dentry_drop)
|
if (ret < 0)
|
||||||
releases += ceph_encode_dentry_release(&p, req->r_old_dentry,
|
goto out_err;
|
||||||
|
releases += ret;
|
||||||
|
}
|
||||||
|
if (req->r_old_dentry_drop) {
|
||||||
|
ret = ceph_encode_dentry_release(&p, req->r_old_dentry,
|
||||||
req->r_old_dentry_dir, mds,
|
req->r_old_dentry_dir, mds,
|
||||||
req->r_old_dentry_drop,
|
req->r_old_dentry_drop,
|
||||||
req->r_old_dentry_unless);
|
req->r_old_dentry_unless);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_err;
|
||||||
|
releases += ret;
|
||||||
|
}
|
||||||
if (req->r_old_inode_drop)
|
if (req->r_old_inode_drop)
|
||||||
releases += ceph_encode_inode_release(&p,
|
releases += ceph_encode_inode_release(&p,
|
||||||
d_inode(req->r_old_dentry),
|
d_inode(req->r_old_dentry),
|
||||||
|
|
@ -2815,6 +2872,10 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
ceph_mdsc_free_path((char *)path1, pathlen1);
|
ceph_mdsc_free_path((char *)path1, pathlen1);
|
||||||
out:
|
out:
|
||||||
return msg;
|
return msg;
|
||||||
|
out_err:
|
||||||
|
ceph_msg_put(msg);
|
||||||
|
msg = ERR_PTR(ret);
|
||||||
|
goto out_free2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -565,7 +565,7 @@ static inline void ceph_mdsc_free_path(char *path, int len)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
||||||
int stop_on_nosnap);
|
int for_wire);
|
||||||
|
|
||||||
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);
|
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);
|
||||||
extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session,
|
extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user