]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
prep for ceph_encode_encrypted_fname() fixes
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 15 Feb 2025 04:27:45 +0000 (23:27 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 17 Jun 2025 21:57:49 +0000 (17:57 -0400)
ceph_encode_encrypted_dname() would be better off with plaintext name
already copied into buffer; we'll lift that into the callers on the
next step, which will allow to fix UAF on races with rename; for now
copy it in the very beginning of ceph_encode_encrypted_dname().

That has a pleasant side benefit - we don't need to mess with tmp_buf
anymore (i.e. that's 256 bytes off the stack footprint).

Tested-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/ceph/crypto.c

index 9c70622458800a1e86aff0de1fd1b20eedb34bb1..2aef56fc6275434c23dfef7237a64bebfe4fae74 100644 (file)
@@ -258,31 +258,28 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
 {
        struct ceph_client *cl = ceph_inode_to_client(parent);
        struct inode *dir = parent;
-       struct qstr iname;
+       char *p = buf;
        u32 len;
        int name_len;
        int elen;
        int ret;
        u8 *cryptbuf = NULL;
 
-       iname.name = d_name->name;
-       name_len = d_name->len;
+       memcpy(buf, d_name->name, d_name->len);
+       elen = d_name->len;
+
+       name_len = elen;
 
        /* Handle the special case of snapshot names that start with '_' */
-       if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) &&
-           (iname.name[0] == '_')) {
-               dir = parse_longname(parent, iname.name, &name_len);
+       if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') {
+               dir = parse_longname(parent, p, &name_len);
                if (IS_ERR(dir))
                        return PTR_ERR(dir);
-               iname.name++; /* skip initial '_' */
+               p++; /* skip initial '_' */
        }
-       iname.len = name_len;
 
-       if (!fscrypt_has_encryption_key(dir)) {
-               memcpy(buf, d_name->name, d_name->len);
-               elen = d_name->len;
+       if (!fscrypt_has_encryption_key(dir))
                goto out;
-       }
 
        /*
         * Convert cleartext d_name to ciphertext. If result is longer than
@@ -290,7 +287,7 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
         *
         * See: fscrypt_setup_filename
         */
-       if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) {
+       if (!fscrypt_fname_encrypted_size(dir, name_len, NAME_MAX, &len)) {
                elen = -ENAMETOOLONG;
                goto out;
        }
@@ -303,7 +300,9 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
                goto out;
        }
 
-       ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len);
+       ret = fscrypt_fname_encrypt(dir,
+                                   &(struct qstr)QSTR_INIT(p, name_len),
+                                   cryptbuf, len);
        if (ret) {
                elen = ret;
                goto out;
@@ -324,18 +323,13 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
        }
 
        /* base64 encode the encrypted name */
-       elen = ceph_base64_encode(cryptbuf, len, buf);
-       doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, buf);
+       elen = ceph_base64_encode(cryptbuf, len, p);
+       doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, p);
 
        /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */
        WARN_ON(elen > 240);
-       if ((elen > 0) && (dir != parent)) {
-               char tmp_buf[NAME_MAX];
-
-               elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
-                               elen, buf, dir->i_ino);
-               memcpy(buf, tmp_buf, elen);
-       }
+       if (dir != parent) // leading _ is already there; append _<inum>
+               elen += 1 + sprintf(p + elen, "_%ld", dir->i_ino);
 
 out:
        kfree(cryptbuf);