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:
parent
f4e60e6b30
commit
3a60a1686f
1 changed files with 52 additions and 48 deletions
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue