eCryptfs: Decrypt symlink target for stat size

Create a getattr handler for eCryptfs symlinks that is capable of
reading the lower target and decrypting its path.  Prior to this patch,
a stat's st_size field would represent the strlen of the encrypted path,
while readlink() would return the strlen of the decrypted path.  This
could lead to confusion in some userspace applications, since the two
values should be equal.

https://bugs.launchpad.net/bugs/524919

Reported-by: Loïc Minier <loic.minier@canonical.com>
Cc: stable@kernel.org
Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
This commit is contained in:
Tyler Hicks 2010-03-22 00:41:35 -05:00
parent f4e60e6b30
commit 3a60a1686f

View file

@ -648,38 +648,17 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return rc;
}
static int
ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
size_t *bufsiz)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
char *lower_buf;
size_t lower_bufsiz;
struct dentry *lower_dentry;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
char *plaintext_name;
size_t plaintext_name_size;
size_t lower_bufsiz = PATH_MAX;
mm_segment_t old_fs;
int rc;
lower_dentry = ecryptfs_dentry_to_lower(dentry);
if (!lower_dentry->d_inode->i_op->readlink) {
rc = -EINVAL;
goto out;
}
mount_crypt_stat = &ecryptfs_superblock_to_private(
dentry->d_sb)->mount_crypt_stat;
/*
* If the lower filename is encrypted, it will result in a significantly
* longer name. If needed, truncate the name after decode and decrypt.
*/
if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
lower_bufsiz = PATH_MAX;
else
lower_bufsiz = bufsiz;
/* Released in this function */
lower_buf = kmalloc(lower_bufsiz, GFP_KERNEL);
if (lower_buf == NULL) {
printk(KERN_ERR "%s: Out of memory whilst attempting to "
"kmalloc [%zd] bytes\n", __func__, lower_bufsiz);
if (!lower_buf) {
rc = -ENOMEM;
goto out;
}
@ -689,29 +668,31 @@ ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
(char __user *)lower_buf,
lower_bufsiz);
set_fs(old_fs);
if (rc >= 0) {
rc = ecryptfs_decode_and_decrypt_filename(&plaintext_name,
&plaintext_name_size,
dentry, lower_buf,
rc);
if (rc) {
printk(KERN_ERR "%s: Error attempting to decode and "
"decrypt filename; rc = [%d]\n", __func__,
rc);
goto out_free_lower_buf;
}
/* Check for bufsiz <= 0 done in sys_readlinkat() */
rc = copy_to_user(buf, plaintext_name,
min((size_t) bufsiz, plaintext_name_size));
if (rc)
rc = -EFAULT;
else
rc = plaintext_name_size;
kfree(plaintext_name);
fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode);
}
out_free_lower_buf:
if (rc < 0)
goto out;
lower_bufsiz = rc;
rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry,
lower_buf, lower_bufsiz);
out:
kfree(lower_buf);
return rc;
}
static int
ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
{
char *kbuf;
size_t kbufsiz, copied;
int rc;
rc = ecryptfs_readlink_lower(dentry, &kbuf, &kbufsiz);
if (rc)
goto out;
copied = min_t(size_t, bufsiz, kbufsiz);
rc = copy_to_user(buf, kbuf, copied) ? -EFAULT : copied;
kfree(kbuf);
fsstack_copy_attr_atime(dentry->d_inode,
ecryptfs_dentry_to_lower(dentry)->d_inode);
out:
return rc;
}
@ -1016,6 +997,28 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
return rc;
}
int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
int rc = 0;
mount_crypt_stat = &ecryptfs_superblock_to_private(
dentry->d_sb)->mount_crypt_stat;
generic_fillattr(dentry->d_inode, stat);
if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
char *target;
size_t targetsiz;
rc = ecryptfs_readlink_lower(dentry, &target, &targetsiz);
if (!rc) {
kfree(target);
stat->size = targetsiz;
}
}
return rc;
}
int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
@ -1133,6 +1136,7 @@ const struct inode_operations ecryptfs_symlink_iops = {
.put_link = ecryptfs_put_link,
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
.getattr = ecryptfs_getattr_link,
.setxattr = ecryptfs_setxattr,
.getxattr = ecryptfs_getxattr,
.listxattr = ecryptfs_listxattr,