Commit 3a60a168 authored by Tyler Hicks's avatar Tyler Hicks

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/524919Reported-by: default avatarLoïc Minier <loic.minier@canonical.com>
Cc: stable@kernel.org
Signed-off-by: default avatarTyler Hicks <tyhicks@linux.vnet.ibm.com>
parent f4e60e6b
...@@ -648,38 +648,17 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -648,38 +648,17 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return rc; return rc;
} }
static int static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) size_t *bufsiz)
{ {
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
char *lower_buf; char *lower_buf;
size_t lower_bufsiz; size_t lower_bufsiz = PATH_MAX;
struct dentry *lower_dentry;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
char *plaintext_name;
size_t plaintext_name_size;
mm_segment_t old_fs; mm_segment_t old_fs;
int rc; 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); lower_buf = kmalloc(lower_bufsiz, GFP_KERNEL);
if (lower_buf == NULL) { if (!lower_buf) {
printk(KERN_ERR "%s: Out of memory whilst attempting to "
"kmalloc [%zd] bytes\n", __func__, lower_bufsiz);
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
} }
...@@ -689,29 +668,31 @@ ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ...@@ -689,29 +668,31 @@ ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
(char __user *)lower_buf, (char __user *)lower_buf,
lower_bufsiz); lower_bufsiz);
set_fs(old_fs); set_fs(old_fs);
if (rc >= 0) { if (rc < 0)
rc = ecryptfs_decode_and_decrypt_filename(&plaintext_name, goto out;
&plaintext_name_size, lower_bufsiz = rc;
dentry, lower_buf, rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry,
rc); lower_buf, lower_bufsiz);
if (rc) { out:
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:
kfree(lower_buf); 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: out:
return rc; return rc;
} }
...@@ -1016,6 +997,28 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) ...@@ -1016,6 +997,28 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
return rc; 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, int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat) struct kstat *stat)
{ {
...@@ -1133,6 +1136,7 @@ const struct inode_operations ecryptfs_symlink_iops = { ...@@ -1133,6 +1136,7 @@ const struct inode_operations ecryptfs_symlink_iops = {
.put_link = ecryptfs_put_link, .put_link = ecryptfs_put_link,
.permission = ecryptfs_permission, .permission = ecryptfs_permission,
.setattr = ecryptfs_setattr, .setattr = ecryptfs_setattr,
.getattr = ecryptfs_getattr_link,
.setxattr = ecryptfs_setxattr, .setxattr = ecryptfs_setxattr,
.getxattr = ecryptfs_getxattr, .getxattr = ecryptfs_getxattr,
.listxattr = ecryptfs_listxattr, .listxattr = ecryptfs_listxattr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment