Received: by 2002:a25:2c96:0:0:0:0:0 with SMTP id s144csp1663285ybs; Tue, 26 May 2020 00:02:41 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwRuiT7gsS7L+FK6HOdRPHH5w3M0EgWrkksmPbaEV1oZpgbsN5XqqmQpZ9IEwQRbvyTKxbb X-Received: by 2002:a17:906:9516:: with SMTP id u22mr20921794ejx.115.1590476560867; Tue, 26 May 2020 00:02:40 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1590476560; cv=none; d=google.com; s=arc-20160816; b=OOfHeWXbFDYqjc4gx5gGduOjW2/XazCQVPn4+9uAvUYtN3M70kpVXLT4Rmbl7MQPIY Ig11KpG2xctCrtYlXvTA7Ju6vgoslfkkfd8ZYtKpsf62Zgyyc3dawd28wAzdJu2UxHAe 99h7oufQy+dEaWq2AYMf2Dp/ys3OOjn3rsR4oTvVp/lTb9yTah7gAwEgTmre1KxW0rO+ YbSns1cjf4I2HzGHpKyzOOMQrrfzLCPT3GZWNclQ0/fhW0OcwxhZ/Q/tEP8GJJgGvcyG YUDypZePOOkfBEgk+N6jY656USrkcVGo6KLPU04g5YdfjqKr70q1dTjLC8ofE42nrv2A es5Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=jwUwNbR5CqkZfKXp34MLLr2FdLANCXsmldjRmcZbcAY=; b=zeOx2jMfQJM4Vz7nlpwaYj9at56nbY3kwSo2CG9MnyI6BymVkW+I0r7F4YlFwh/tjs 5ZmsixsUFL3aKPphUL+dVxqEywtEECIYO1Odd01DQ1zztKMytQK8KzSTJigK1vPBLfct gZWZk8kGJ0NOG7yYAH9BWCfIuryRZ2gAG7Fav3mjT8e/wvvII6HPHJRcJ06TOTQDDWah 8q4DDfP0bFyAOg/X6LkWqXFjPPuNHDJ2wTr53lbqAmZiTxWviT+sYlJstywfomuxUjbS vDoCazhEJoYOYdUm8K4NFkRBut/Zo097cbk3kn/oYYVROhzJNAQOoLAViU+B9typbMTp 0qOQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@sargun.me header.s=google header.b=IMtvqyNC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id ck20si11001680ejb.528.2020.05.26.00.02.16; Tue, 26 May 2020 00:02:40 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@sargun.me header.s=google header.b=IMtvqyNC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731322AbgEZG76 (ORCPT + 99 others); Tue, 26 May 2020 02:59:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33370 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728172AbgEZG75 (ORCPT ); Tue, 26 May 2020 02:59:57 -0400 Received: from mail-ej1-x643.google.com (mail-ej1-x643.google.com [IPv6:2a00:1450:4864:20::643]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 500F3C03E97E for ; Mon, 25 May 2020 23:59:56 -0700 (PDT) Received: by mail-ej1-x643.google.com with SMTP id e2so22563391eje.13 for ; Mon, 25 May 2020 23:59:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=jwUwNbR5CqkZfKXp34MLLr2FdLANCXsmldjRmcZbcAY=; b=IMtvqyNCddHdr+Dg+hG8KQcXr84JmNxxVimpszlEfUHrcjBDVoDh3Gvy9ORJbxEgV0 vZea+oDkn72s0xoPFO4YOowXE9LgGhrgQ3WpNcxKR/0ui1LiB/AQUz74OPqDFogZYq8J 0hihwxkFMa3ny3Gt0tpDDjn2bRZAeYr4bxIAY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=jwUwNbR5CqkZfKXp34MLLr2FdLANCXsmldjRmcZbcAY=; b=sz3HAaqP6LHF7vOD7xjHwNakTxQjy6xnMdND627Sk/22b46+OimNv0vPTAXsR1G9pz zOuJlOWijBfZlAlSZmVzL+pi882I7+yUVYY6D7QHTh/0/8hFZTwu+Jk/LKDYjeOceiZK 9UOlxqVLKM7PQDhVNzd+pz3LXBXiKyAnET4+I40YSh1kbPfmzhFQYIr4p03ksrUWHjGv tFdLYHzwAxNor+ke2FBzqWGfQx1ZNLUaqxsVbNepUtszK9D28CT8gsvOsFQmR9RymKxJ y50l40P29iyf1+9I/1Yp6RsbS4sHaiLTNtrDHlZdwjOeJU0O6QkPvY/Mo+u9WZy6FlzZ e32A== X-Gm-Message-State: AOAM532uXcCMRDQ2R1FNe9gTKD591ARmwx30sOTBB7pybkygF2u0dKIe Dir7wf4wqSgEVf6gpJs4NdlAPGrQLn+AcucIoX91Yw== X-Received: by 2002:a17:907:1002:: with SMTP id ox2mr20982901ejb.189.1590476394778; Mon, 25 May 2020 23:59:54 -0700 (PDT) MIME-Version: 1.0 References: <20200524233942.8702-1-sargun@sargun.me> <20200524233942.8702-3-sargun@sargun.me> <20200525135036.vp2nmmx42y7dfznf@wittgenstein> In-Reply-To: <20200525135036.vp2nmmx42y7dfznf@wittgenstein> From: Sargun Dhillon Date: Mon, 25 May 2020 23:59:18 -0700 Message-ID: Subject: Re: [PATCH 2/5] seccomp: Introduce addfd ioctl to seccomp user notifier To: Christian Brauner Cc: LKML , Linux Containers , Linux API , Tycho Andersen , Kees Cook , Aleksa Sarai , Jeffrey Vander Stoep , Jann Horn , Robert Sesek , Chris Palmer , Matt Denton , Kees Cook Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, May 25, 2020 at 6:50 AM Christian Brauner wrote: > > On Sun, May 24, 2020 at 04:39:39PM -0700, Sargun Dhillon wrote: > > This adds a seccomp notifier ioctl which allows for the listener to "add" > > file descriptors to a process which originated a seccomp user > > notification. This allows calls like mount, and mknod to be "implemented", > > as the return value, and the arguments are data in memory. On the other > > hand, calls like connect can be "implemented" using pidfd_getfd. > > > > Unfortunately, there are calls which return file descriptors, like > > open, which are vulnerable to TOC-TOU attacks, and require that the > > more privileged supervisor can inspect the argument, and perform the > > syscall on behalf of the process generating the notifiation. This > > allows the file descriptor generated from that open call to be > > returned to the calling process. > > > > In addition, there is funcitonality to allow for replacement of > > specific file descriptors, following dup2-like semantics. > > > > Signed-off-by: Sargun Dhillon > > Suggested-by: Matt Denton > > Cc: Kees Cook , > > Cc: Jann Horn , > > Cc: Robert Sesek , > > Cc: Chris Palmer > > Cc: Christian Brauner > > Cc: Tycho Andersen > > --- > > include/uapi/linux/seccomp.h | 25 ++++++ > > kernel/seccomp.c | 169 ++++++++++++++++++++++++++++++++++- > > 2 files changed, 193 insertions(+), 1 deletion(-) > > > > diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h > > index c1735455bc53..7d450a9e4c29 100644 > > --- a/include/uapi/linux/seccomp.h > > +++ b/include/uapi/linux/seccomp.h > > @@ -113,6 +113,27 @@ struct seccomp_notif_resp { > > __u32 flags; > > }; > > > > +/* valid flags for seccomp_notif_addfd */ > > +#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0) /* Specify remote fd */ > > + > > +/** > > + * struct seccomp_notif_addfd > > + * @size: The size of the seccomp_notif_addfd datastructure > > + * @fd: The local fd number > > + * @id: The ID of the seccomp notification > > + * @fd_flags: Flags the remote FD should be allocated under > > + * @remote_fd: Optional remote FD number if SETFD option is set, otherwise 0. > > + * @flags: SECCOMP_ADDFD_FLAG_* > > + */ > > +struct seccomp_notif_addfd { > > + __u32 size; > > + __u32 fd; > > + __u64 id; > > + __u32 fd_flags; > > + __u32 remote_fd; > > + __u64 flags; > > +}; > > This was a little confusing to me at first. So fd is the fd from which > we take the struct file and remote_fd is either -1 at which point we > just allocate the next free fd number and if it is not we > allocate/replace a specific one. Maybe it would be clearer if we did: > > struct seccomp_notif_addfd { > __u32 size; > __u64 id; > __u64 flags; > __u32 srcfd; > __u32 newfd; > __u32 newfd_flags; > }; > > No need to hide in the name that this is remote_dup2(). > > > + > > #define SECCOMP_IOC_MAGIC '!' > > #define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr) > > #define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type) > > @@ -124,4 +145,8 @@ struct seccomp_notif_resp { > > #define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \ > > struct seccomp_notif_resp) > > #define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOR(2, __u64) > > +/* On success, the return value is the remote process's added fd number */ > > +#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOR(3, \ > > + struct seccomp_notif_addfd) > > + > > #endif /* _UAPI_LINUX_SECCOMP_H */ > > diff --git a/kernel/seccomp.c b/kernel/seccomp.c > > index f6ce94b7a167..88940eeabaee 100644 > > --- a/kernel/seccomp.c > > +++ b/kernel/seccomp.c > > @@ -77,10 +77,42 @@ struct seccomp_knotif { > > long val; > > u32 flags; > > > > - /* Signals when this has entered SECCOMP_NOTIFY_REPLIED */ > > + /* > > + * Signals when this has changed states, such as the listener > > + * dying, a new seccomp addfd message, or changing to REPLIED > > + */ > > struct completion ready; > > > > struct list_head list; > > + > > + /* outstanding addfd requests */ > > + struct list_head addfd; > > +}; > > + > > +/** > > + * struct seccomp_kaddfd - contianer for seccomp_addfd ioctl messages > > ^^^ typo > > > + * > > + * @file: A reference to the file to install in the other task > > + * @fd: The fd number to install it at. If the fd number is -1, it means the > > + * installing process should allocate the fd as normal. > > + * @flags: The flags for the new file descriptor. At the moment, only O_CLOEXEC > > + * is allowed. > > + * @ret: The return value of the installing process. It is set to the fd num > > + * upon success (>= 0). > > + * @completion: Indicates that the installing process has completed fd > > + * installation, or gone away (either due to successful > > + * reply, or signal) > > + * > > + */ > > +struct seccomp_kaddfd { > > + struct file *file; > > + int fd; > > + unsigned int flags; > > + > > + /* To only be set on reply */ > > + int ret; > > + struct completion completion; > > + struct list_head list; > > }; > > > > /** > > @@ -735,6 +767,35 @@ static u64 seccomp_next_notify_id(struct seccomp_filter *filter) > > return filter->notif->next_id++; > > } > > > > +static void seccomp_handle_addfd(struct seccomp_kaddfd *addfd) > > +{ > > + int ret; > > + > > + /* > > + * Remove the notification, and reset the list pointers, indicating > > + * that it has been handled. > > + */ > > + list_del_init(&addfd->list); > > + > > + ret = security_file_receive(addfd->file); > > + if (ret) > > + goto out; > > + > > + if (addfd->fd >= 0) { > > + ret = replace_fd(addfd->fd, addfd->file, addfd->flags); > > + if (ret >= 0) > > + fput(addfd->file); > > + } else { > > + ret = get_unused_fd_flags(addfd->flags); > > + if (ret >= 0) > > + fd_install(ret, addfd->file); > > + } > > + > > +out: > > + addfd->ret = ret; > > + complete(&addfd->completion); > > +} > > + > > static int seccomp_do_user_notification(int this_syscall, > > struct seccomp_filter *match, > > const struct seccomp_data *sd) > > @@ -743,6 +804,7 @@ static int seccomp_do_user_notification(int this_syscall, > > u32 flags = 0; > > long ret = 0; > > struct seccomp_knotif n = {}; > > + struct seccomp_kaddfd *addfd, *tmp; > > > > mutex_lock(&match->notify_lock); > > err = -ENOSYS; > > @@ -755,6 +817,7 @@ static int seccomp_do_user_notification(int this_syscall, > > n.id = seccomp_next_notify_id(match); > > init_completion(&n.ready); > > list_add(&n.list, &match->notif->notifications); > > + INIT_LIST_HEAD(&n.addfd); > > > > up(&match->notif->request); > > wake_up_poll(&match->notif->wqh, EPOLLIN | EPOLLRDNORM); > > @@ -763,14 +826,31 @@ static int seccomp_do_user_notification(int this_syscall, > > /* > > * This is where we wait for a reply from userspace. > > */ > > +wait: > > err = wait_for_completion_interruptible(&n.ready); > > mutex_lock(&match->notify_lock); > > if (err == 0) { > > + /* Check if we were woken up by a addfd message */ > > + addfd = list_first_entry_or_null(&n.addfd, > > + struct seccomp_kaddfd, list); > > + if (addfd && n.state != SECCOMP_NOTIFY_REPLIED) { > > + seccomp_handle_addfd(addfd); > > + mutex_unlock(&match->notify_lock); > > + goto wait; > > + } > > ret = n.val; > > err = n.error; > > flags = n.flags; > > } > > > > + /* If there were any pending addfd calls, clear them out */ > > + list_for_each_entry_safe(addfd, tmp, &n.addfd, list) { > > + /* The process went away before we got a chance to handle it */ > > + addfd->ret = -ENOENT; > > Looks like it should be -ESRCH? > I'm a little confused on where we use ESRCH vs. ENOENT. It looks like in the cookie check (SECCOMP_IOCTL_NOTIF_ID_VALID), we return ENOENT on both error paths -- whether the notification is missing, or whether the notification was already replied to. I originally had this as ESRCH, but switched to ENOENT to be consistent with that API. Do we want the API to disclose information about half-done / incomplete notifications?