Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CALCETrUCQzY7vuYS6NZDUA04QMYZW5srP0FzE26a8-qp1K-P_w@mail.gmail.com>
Date: Sun, 24 Jun 2018 17:46:21 -0700
From: Andy Lutomirski <luto@...nel.org>
To: dgilbert@...erlog.com
Cc: Jann Horn <jannh@...gle.com>, jejb@...ux.vnet.ibm.com, 
	"Martin K. Petersen" <martin.petersen@...cle.com>, Linux SCSI List <linux-scsi@...r.kernel.org>, 
	Christoph Hellwig <hch@...radead.org>, Al Viro <viro@...iv.linux.org.uk>, 
	LKML <linux-kernel@...r.kernel.org>, Jens Axboe <axboe@...nel.dk>, 
	fujita.tomonori@....ntt.co.jp, 
	Kernel Hardening <kernel-hardening@...ts.openwall.com>, security@...nel.org, 
	bblock@...ux.vnet.ibm.com
Subject: Re: [PATCH v2] sg: mitigate read/write abuse

On Sat, Jun 23, 2018 at 3:06 PM Douglas Gilbert <dgilbert@...erlog.com> wrote:
>
> On 2018-06-21 08:56 PM, Jann Horn wrote:
> > On Thu, Jun 21, 2018 at 6:53 PM Douglas Gilbert <dgilbert@...erlog.com> wrote:
> >>
> >> On 2018-06-21 05:18 PM, Jann Horn wrote:
> >>> As Al Viro noted in commit 128394eff343 ("sg_write()/bsg_write() is not fit
> >>> to be called under KERNEL_DS"), sg improperly accesses userspace memory
> >>> outside the provided buffer, permitting kernel memory corruption via
> >>> splice().
> >>> But it doesn't just do it on ->write(), also on ->read().
> >>>
> >>> As a band-aid, make sure that the ->read() and ->write() handlers can not
> >>> be called in weird contexts (kernel context or credentials different from
> >>> file opener), like for ib_safe_file_access().
> >>>
> >>> If someone needs to use these interfaces from different security contexts,
> >>> a new interface should be written that goes through the ->ioctl() handler.
> >>>
> >>> I've mostly copypasted ib_safe_file_access() over as sg_safe_file_access()
> >>> because I couldn't find a good common header - please tell me if you know a
> >>> better way.
> >>> The duplicate pr_err_once() calls are so that each of them fires once;
> >>> otherwise, this would probably have to be a macro.
> >>>
> >>> changed in v2:
> >>>    - remove the bsg parts per Christoph Hellwig's request
> >>>
> >>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> >>> Cc: <stable@...r.kernel.org>
> >>> Signed-off-by: Jann Horn <jannh@...gle.com>
> >>> ---
> >>>    drivers/scsi/sg.c | 29 ++++++++++++++++++++++++++++-
> >>>    1 file changed, 28 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
> >>> index 53ae52dbff84..51b685192646 100644
> >>> --- a/drivers/scsi/sg.c
> >>> +++ b/drivers/scsi/sg.c
> >>> @@ -51,6 +51,7 @@ static int sg_version_num = 30536;  /* 2 digits for each component */
> >>>    #include <linux/atomic.h>
> >>>    #include <linux/ratelimit.h>
> >>>    #include <linux/uio.h>
> >>> +#include <linux/cred.h> /* for sg_safe_file_access() */
> >>>
> >>>    #include "scsi.h"
> >>>    #include <scsi/scsi_dbg.h>
> >>> @@ -209,6 +210,23 @@ static void sg_device_destroy(struct kref *kref);
> >>>        sdev_prefix_printk(prefix, (sdp)->device,               \
> >>>                           (sdp)->disk->disk_name, fmt, ##a)
> >>>
> >>> +/*
> >>> + * The SCSI interfaces that use read() and write() as an asynchronous variant of
> >>> + * ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways
> >>> + * to trigger read() and write() calls from various contexts with elevated
> >>> + * privileges. This can lead to kernel memory corruption (e.g. if these
> >>> + * interfaces are called through splice()) and privilege escalation inside
> >>> + * userspace (e.g. if a process with access to such a device passes a file
> >>> + * descriptor to a SUID binary as stdin/stdout/stderr).
> >>> + *
> >>> + * This function provides protection for the legacy API by restricting the
> >>> + * calling context.
> >>> + */
> >>> +static inline bool sg_safe_file_access(struct file *filp)
> >>> +{
> >>> +     return filp->f_cred == current_cred() && !uaccess_kernel();
> >>> +}
> >>> +
> >>>    static int sg_allow_access(struct file *filp, unsigned char *cmd)
> >>>    {
> >>>        struct sg_fd *sfp = filp->private_data;
> >>> @@ -393,6 +411,12 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
> >>>        struct sg_header *old_hdr = NULL;
> >>>        int retval = 0;
> >>>
> >>> +     if (!sg_safe_file_access(filp)) {
> >>> +             pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
> >>> +                     __func__, task_tgid_vnr(current), current->comm);
> >>> +             return -EINVAL;
> >>
> >> The error message and returned code apply to the
> >> (filp->f_cred == current_cred()) case, not so much to !uaccess_kernel().
> >> While on the error path could you not break out the !uaccess_kernel()
> >> with an appropriate error message and a return code of -EACCES ? Perhaps
> >> a message is unneeded since EACCES is clear.
> >>
> >> Not that wild about EINVAL either since it suggests (to me) a "front end"
> >> error (e.g. associated with a badly formed request). How about EPERM for
> >> the changing credentials case.
> >
> > I used EINVAL since infiniband uses that error case, but I see how it
> > would be a relatively confusing error code in the context of an sg
> > device - I agree that EACCES and EPERM might be a better fit here.
> > I'll adjust the patch.
> > However, shouldn't it be EPERM in the uaccess_kernel() case and EACCES
> > in the filp->f_cred!=current_cred() case (instead of the other way
> > around)?
>
> NO!
>
> See 'man errno':
>     EACCES     Permission denied
>     EPERM      Operation not permitted
>

Usually EPERM means the caller doesn't have access and EACCES means
the fd doesn't have access.

What we really want is -EDRIVERISAPIECEOFCRAP.

--Andy

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.