Skip to content

Commit af9ffa6

Browse files
lxbszidryomov
authored andcommitted
ceph: add support to readdir for encrypted names
To make it simpler to decrypt names in a readdir reply (i.e. before we have a dentry), add a new ceph_encode_encrypted_fname()-like helper that takes a qstr pointer instead of a dentry pointer. Once we've decrypted the names in a readdir reply, we no longer need the crypttext, so overwrite them in ceph_mds_reply_dir_entry with the unencrypted names. Then in both ceph_readdir_prepopulate() and ceph_readdir() we will use the dencrypted name directly. [ jlayton: convert some BUG_ONs into error returns ] Signed-off-by: Xiubo Li <xiubli@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> 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 3859af9 commit af9ffa6

6 files changed

Lines changed: 144 additions & 26 deletions

File tree

fs/ceph/crypto.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,26 @@ void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
192192
swap(req->r_fscrypt_auth, as->fscrypt_auth);
193193
}
194194

195-
int ceph_encode_encrypted_fname(const struct inode *parent,
196-
struct dentry *dentry, char *buf)
195+
int ceph_encode_encrypted_dname(const struct inode *parent,
196+
struct qstr *d_name, char *buf)
197197
{
198198
u32 len;
199199
int elen;
200200
int ret;
201201
u8 *cryptbuf;
202202

203-
WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
203+
if (!fscrypt_has_encryption_key(parent)) {
204+
memcpy(buf, d_name->name, d_name->len);
205+
return d_name->len;
206+
}
204207

205208
/*
206209
* Convert cleartext d_name to ciphertext. If result is longer than
207210
* CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes
208211
*
209212
* See: fscrypt_setup_filename
210213
*/
211-
if (!fscrypt_fname_encrypted_size(parent, dentry->d_name.len, NAME_MAX,
212-
&len))
214+
if (!fscrypt_fname_encrypted_size(parent, d_name->len, NAME_MAX, &len))
213215
return -ENAMETOOLONG;
214216

215217
/* Allocate a buffer appropriate to hold the result */
@@ -218,7 +220,7 @@ int ceph_encode_encrypted_fname(const struct inode *parent,
218220
if (!cryptbuf)
219221
return -ENOMEM;
220222

221-
ret = fscrypt_fname_encrypt(parent, &dentry->d_name, cryptbuf, len);
223+
ret = fscrypt_fname_encrypt(parent, d_name, cryptbuf, len);
222224
if (ret) {
223225
kfree(cryptbuf);
224226
return ret;
@@ -245,6 +247,14 @@ int ceph_encode_encrypted_fname(const struct inode *parent,
245247
return elen;
246248
}
247249

250+
int ceph_encode_encrypted_fname(const struct inode *parent,
251+
struct dentry *dentry, char *buf)
252+
{
253+
WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
254+
255+
return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf);
256+
}
257+
248258
/**
249259
* ceph_fname_to_usr - convert a filename for userland presentation
250260
* @fname: ceph_fname to be converted
@@ -286,7 +296,10 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
286296
* generating a nokey name via fscrypt.
287297
*/
288298
if (!fscrypt_has_encryption_key(fname->dir)) {
289-
memcpy(oname->name, fname->name, fname->name_len);
299+
if (fname->no_copy)
300+
oname->name = fname->name;
301+
else
302+
memcpy(oname->name, fname->name, fname->name_len);
290303
oname->len = fname->name_len;
291304
if (is_nokey)
292305
*is_nokey = true;

fs/ceph/crypto.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct ceph_fname {
1919
unsigned char *ctext; // binary crypttext (if any)
2020
u32 name_len; // length of name buffer
2121
u32 ctext_len; // length of crypttext
22+
bool no_copy;
2223
};
2324

2425
struct ceph_fscrypt_auth {
@@ -76,6 +77,8 @@ int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
7677
struct ceph_acl_sec_ctx *as);
7778
void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
7879
struct ceph_acl_sec_ctx *as);
80+
int ceph_encode_encrypted_dname(const struct inode *parent,
81+
struct qstr *d_name, char *buf);
7982
int ceph_encode_encrypted_fname(const struct inode *parent,
8083
struct dentry *dentry, char *buf);
8184

@@ -121,6 +124,13 @@ static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
121124
{
122125
}
123126

127+
static inline int ceph_encode_encrypted_dname(const struct inode *parent,
128+
struct qstr *d_name, char *buf)
129+
{
130+
memcpy(buf, d_name->name, d_name->len);
131+
return d_name->len;
132+
}
133+
124134
static inline int ceph_encode_encrypted_fname(const struct inode *parent,
125135
struct dentry *dentry, char *buf)
126136
{

fs/ceph/dir.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "super.h"
1111
#include "mds_client.h"
12+
#include "crypto.h"
1213

1314
/*
1415
* Directory operations: readdir, lookup, create, link, unlink,
@@ -241,7 +242,9 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx,
241242
di = ceph_dentry(dentry);
242243
if (d_unhashed(dentry) ||
243244
d_really_is_negative(dentry) ||
244-
di->lease_shared_gen != shared_gen) {
245+
di->lease_shared_gen != shared_gen ||
246+
((dentry->d_flags & DCACHE_NOKEY_NAME) &&
247+
fscrypt_has_encryption_key(dir))) {
245248
spin_unlock(&dentry->d_lock);
246249
dput(dentry);
247250
err = -EAGAIN;
@@ -340,6 +343,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
340343
ctx->pos = 2;
341344
}
342345

346+
err = fscrypt_prepare_readdir(inode);
347+
if (err)
348+
return err;
349+
343350
spin_lock(&ci->i_ceph_lock);
344351
/* request Fx cap. if have Fx, we don't need to release Fs cap
345352
* for later create/unlink. */
@@ -389,6 +396,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
389396
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
390397
if (IS_ERR(req))
391398
return PTR_ERR(req);
399+
392400
err = ceph_alloc_readdir_reply_buffer(req, inode);
393401
if (err) {
394402
ceph_mdsc_put_request(req);
@@ -402,11 +410,21 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
402410
req->r_inode_drop = CEPH_CAP_FILE_EXCL;
403411
}
404412
if (dfi->last_name) {
405-
req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL);
413+
struct qstr d_name = { .name = dfi->last_name,
414+
.len = strlen(dfi->last_name) };
415+
416+
req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL);
406417
if (!req->r_path2) {
407418
ceph_mdsc_put_request(req);
408419
return -ENOMEM;
409420
}
421+
422+
err = ceph_encode_encrypted_dname(inode, &d_name,
423+
req->r_path2);
424+
if (err < 0) {
425+
ceph_mdsc_put_request(req);
426+
return err;
427+
}
410428
} else if (is_hash_order(ctx->pos)) {
411429
req->r_args.readdir.offset_hash =
412430
cpu_to_le32(fpos_hash(ctx->pos));
@@ -511,15 +529,20 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
511529
for (; i < rinfo->dir_nr; i++) {
512530
struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
513531

514-
BUG_ON(rde->offset < ctx->pos);
532+
if (rde->offset < ctx->pos) {
533+
pr_warn("%s: rde->offset 0x%llx ctx->pos 0x%llx\n",
534+
__func__, rde->offset, ctx->pos);
535+
return -EIO;
536+
}
537+
538+
if (WARN_ON_ONCE(!rde->inode.in))
539+
return -EIO;
515540

516541
ctx->pos = rde->offset;
517542
dout("readdir (%d/%d) -> %llx '%.*s' %p\n",
518543
i, rinfo->dir_nr, ctx->pos,
519544
rde->name_len, rde->name, &rde->inode.in);
520545

521-
BUG_ON(!rde->inode.in);
522-
523546
if (!dir_emit(ctx, rde->name, rde->name_len,
524547
ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)),
525548
le32_to_cpu(rde->inode.in->mode) >> 12)) {
@@ -532,6 +555,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
532555
dout("filldir stopping us...\n");
533556
return 0;
534557
}
558+
559+
/* Reset the lengths to their original allocated vals */
535560
ctx->pos++;
536561
}
537562

@@ -586,7 +611,6 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
586611
dfi->dir_ordered_count);
587612
spin_unlock(&ci->i_ceph_lock);
588613
}
589-
590614
dout("readdir %p file %p done.\n", inode, file);
591615
return 0;
592616
}

fs/ceph/inode.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
17521752
struct ceph_mds_session *session)
17531753
{
17541754
struct dentry *parent = req->r_dentry;
1755-
struct ceph_inode_info *ci = ceph_inode(d_inode(parent));
1755+
struct inode *inode = d_inode(parent);
1756+
struct ceph_inode_info *ci = ceph_inode(inode);
17561757
struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
17571758
struct qstr dname;
17581759
struct dentry *dn;
@@ -1826,9 +1827,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
18261827
tvino.snap = le64_to_cpu(rde->inode.in->snapid);
18271828

18281829
if (rinfo->hash_order) {
1829-
u32 hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash,
1830-
rde->name, rde->name_len);
1831-
hash = ceph_frag_value(hash);
1830+
u32 hash = ceph_frag_value(rde->raw_hash);
18321831
if (hash != last_hash)
18331832
fpos_offset = 2;
18341833
last_hash = hash;
@@ -1851,6 +1850,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
18511850
err = -ENOMEM;
18521851
goto out;
18531852
}
1853+
if (rde->is_nokey) {
1854+
spin_lock(&dn->d_lock);
1855+
dn->d_flags |= DCACHE_NOKEY_NAME;
1856+
spin_unlock(&dn->d_lock);
1857+
}
18541858
} else if (d_really_is_positive(dn) &&
18551859
(ceph_ino(d_inode(dn)) != tvino.ino ||
18561860
ceph_snap(d_inode(dn)) != tvino.snap)) {

fs/ceph/mds_client.c

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -440,20 +440,87 @@ static int parse_reply_info_readdir(void **p, void *end,
440440

441441
info->dir_nr = num;
442442
while (num) {
443+
struct inode *inode = d_inode(req->r_dentry);
444+
struct ceph_inode_info *ci = ceph_inode(inode);
443445
struct ceph_mds_reply_dir_entry *rde = info->dir_entries + i;
446+
struct fscrypt_str tname = FSTR_INIT(NULL, 0);
447+
struct fscrypt_str oname = FSTR_INIT(NULL, 0);
448+
struct ceph_fname fname;
449+
u32 altname_len, _name_len;
450+
u8 *altname, *_name;
451+
444452
/* dentry */
445-
ceph_decode_32_safe(p, end, rde->name_len, bad);
446-
ceph_decode_need(p, end, rde->name_len, bad);
447-
rde->name = *p;
448-
*p += rde->name_len;
449-
dout("parsed dir dname '%.*s'\n", rde->name_len, rde->name);
453+
ceph_decode_32_safe(p, end, _name_len, bad);
454+
ceph_decode_need(p, end, _name_len, bad);
455+
_name = *p;
456+
*p += _name_len;
457+
dout("parsed dir dname '%.*s'\n", _name_len, _name);
458+
459+
if (info->hash_order)
460+
rde->raw_hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash,
461+
_name, _name_len);
450462

451463
/* dentry lease */
452464
err = parse_reply_info_lease(p, end, &rde->lease, features,
453-
&rde->altname_len, &rde->altname);
465+
&altname_len, &altname);
454466
if (err)
455467
goto out_bad;
456468

469+
/*
470+
* Try to dencrypt the dentry names and update them
471+
* in the ceph_mds_reply_dir_entry struct.
472+
*/
473+
fname.dir = inode;
474+
fname.name = _name;
475+
fname.name_len = _name_len;
476+
fname.ctext = altname;
477+
fname.ctext_len = altname_len;
478+
/*
479+
* The _name_len maybe larger than altname_len, such as
480+
* when the human readable name length is in range of
481+
* (CEPH_NOHASH_NAME_MAX, CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE),
482+
* then the copy in ceph_fname_to_usr will corrupt the
483+
* data if there has no encryption key.
484+
*
485+
* Just set the no_copy flag and then if there has no
486+
* encryption key the oname.name will be assigned to
487+
* _name always.
488+
*/
489+
fname.no_copy = true;
490+
if (altname_len == 0) {
491+
/*
492+
* Set tname to _name, and this will be used
493+
* to do the base64_decode in-place. It's
494+
* safe because the decoded string should
495+
* always be shorter, which is 3/4 of origin
496+
* string.
497+
*/
498+
tname.name = _name;
499+
500+
/*
501+
* Set oname to _name too, and this will be
502+
* used to do the dencryption in-place.
503+
*/
504+
oname.name = _name;
505+
oname.len = _name_len;
506+
} else {
507+
/*
508+
* This will do the decryption only in-place
509+
* from altname cryptext directly.
510+
*/
511+
oname.name = altname;
512+
oname.len = altname_len;
513+
}
514+
rde->is_nokey = false;
515+
err = ceph_fname_to_usr(&fname, &tname, &oname, &rde->is_nokey);
516+
if (err) {
517+
pr_err("%s unable to decode %.*s, got %d\n", __func__,
518+
_name_len, _name, err);
519+
goto out_bad;
520+
}
521+
rde->name = oname.name;
522+
rde->name_len = oname.len;
523+
457524
/* inode */
458525
err = parse_reply_info_in(p, end, &rde->inode, features);
459526
if (err < 0)
@@ -3671,7 +3738,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
36713738
if (err == 0) {
36723739
if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR ||
36733740
req->r_op == CEPH_MDS_OP_LSSNAP))
3674-
ceph_readdir_prepopulate(req, req->r_session);
3741+
err = ceph_readdir_prepopulate(req, req->r_session);
36753742
}
36763743
current->journal_info = NULL;
36773744
mutex_unlock(&req->r_fill_mutex);

fs/ceph/mds_client.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ struct ceph_mds_reply_info_in {
9696
};
9797

9898
struct ceph_mds_reply_dir_entry {
99+
bool is_nokey;
99100
char *name;
100-
u8 *altname;
101101
u32 name_len;
102-
u32 altname_len;
102+
u32 raw_hash;
103103
struct ceph_mds_reply_lease *lease;
104104
struct ceph_mds_reply_info_in inode;
105105
loff_t offset;

0 commit comments

Comments
 (0)