* Re: [PATCH v2 0/5] pid: add pidfd_open()
From: Florian Weimer @ 2019-04-02 11:03 UTC (permalink / raw)
To: Daniel Colascione
Cc: Linus Torvalds, Jonathan Kowalski, Aleksa Sarai, Andy Lutomirski,
Christian Brauner, Jann Horn, Andrew Lutomirski, David Howells,
Serge E. Hallyn, Linux API, Linux List Kernel Mailing,
Arnd Bergmann, Eric W. Biederman, Konstantin Khlebnikov,
Kees Cook, Alexey Dobriyan, Thomas Gleixner,
Michael Kerrisk-manpages
In-Reply-To: <CAKOZuet2QCoW1XMpTAzXS868PhF-gh14czFn4gwE4A=oX=Yr8w@mail.gmail.com>
* Daniel Colascione:
> But doesn't the CSIGNAL approach still require that libraries somehow
> coordinate which non-SIGCHLD signal they use? (Signal coordination a
> separate problem, unfortunately.)
There's already an allocation mechanism for realtime signals in glibc,
via __libc_allocate_rtsig. I don't know what it is about: It's clearly
intended as an external interface, yet there isn't a declaration in any
installed header file. ALSA has some optional code to use it, but I
don't think distributions compile ALSA in that way; it always uses SIGIO.
Thanks,
Florian
^ permalink raw reply
* Re: [PATCH ghak90 V5 09/10] audit: add support for containerid to network namespaces
From: Neil Horman @ 2019-04-02 11:31 UTC (permalink / raw)
To: Paul Moore
Cc: Richard Guy Briggs, containers, linux-api,
Linux-Audit Mailing List, linux-fsdevel, LKML, netdev,
netfilter-devel, sgrubb, omosnace, dhowells, simo, Eric Paris,
Serge Hallyn, ebiederm
In-Reply-To: <CAHC9VhQpk6cFZckQH+LQmQmirVzLm_GH57ZD3-76y-59G=DocQ@mail.gmail.com>
On Mon, Apr 01, 2019 at 10:50:03AM -0400, Paul Moore wrote:
> On Fri, Mar 15, 2019 at 2:35 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> > Audit events could happen in a network namespace outside of a task
> > context due to packets received from the net that trigger an auditing
> > rule prior to being associated with a running task. The network
> > namespace could be in use by multiple containers by association to the
> > tasks in that network namespace. We still want a way to attribute
> > these events to any potential containers. Keep a list per network
> > namespace to track these audit container identifiiers.
> >
> > Add/increment the audit container identifier on:
> > - initial setting of the audit container identifier via /proc
> > - clone/fork call that inherits an audit container identifier
> > - unshare call that inherits an audit container identifier
> > - setns call that inherits an audit container identifier
> > Delete/decrement the audit container identifier on:
> > - an inherited audit container identifier dropped when child set
> > - process exit
> > - unshare call that drops a net namespace
> > - setns call that drops a net namespace
> >
> > See: https://github.com/linux-audit/audit-kernel/issues/92
> > See: https://github.com/linux-audit/audit-testsuite/issues/64
> > See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
> > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> > ---
> > include/linux/audit.h | 19 ++++++++++++
> > kernel/audit.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
> > kernel/nsproxy.c | 4 +++
> > 3 files changed, 106 insertions(+), 3 deletions(-)
>
> ...
>
> > diff --git a/kernel/audit.c b/kernel/audit.c
> > index cf448599ef34..7fa3194f5342 100644
> > --- a/kernel/audit.c
> > +++ b/kernel/audit.c
> > @@ -72,6 +72,7 @@
> > #include <linux/freezer.h>
> > #include <linux/pid_namespace.h>
> > #include <net/netns/generic.h>
> > +#include <net/net_namespace.h>
> >
> > #include "audit.h"
> >
> > @@ -99,9 +100,13 @@
> > /**
> > * struct audit_net - audit private network namespace data
> > * @sk: communication socket
> > + * @contid_list: audit container identifier list
> > + * @contid_list_lock audit container identifier list lock
> > */
> > struct audit_net {
> > struct sock *sk;
> > + struct list_head contid_list;
> > + spinlock_t contid_list_lock;
> > };
> >
> > /**
> > @@ -275,8 +280,11 @@ struct audit_task_info init_struct_audit = {
> > void audit_free(struct task_struct *tsk)
> > {
> > struct audit_task_info *info = tsk->audit;
> > + struct nsproxy *ns = tsk->nsproxy;
> >
> > audit_free_syscall(tsk);
> > + if (ns)
> > + audit_netns_contid_del(ns->net_ns, audit_get_contid(tsk));
> > /* Freeing the audit_task_info struct must be performed after
> > * audit_log_exit() due to need for loginuid and sessionid.
> > */
> > @@ -376,6 +384,73 @@ static struct sock *audit_get_sk(const struct net *net)
> > return aunet->sk;
> > }
> >
> > +void audit_netns_contid_add(struct net *net, u64 contid)
> > +{
> > + struct audit_net *aunet = net_generic(net, audit_net_id);
> > + struct list_head *contid_list = &aunet->contid_list;
> > + struct audit_contid *cont;
> > +
> > + if (!audit_contid_valid(contid))
> > + return;
> > + if (!aunet)
> > + return;
>
> We should move the contid_list assignment below this check, or decide
> that aunet is always going to valid (?) and get rid of this check
> completely.
>
I'm not sure why that would be needed. Finding the net_id list is an operation
of a map relating net namespaces to lists, not contids to lists. We could do
it, sure, but since they're unrelated operations, I don't think we experience
any slowdowns from doing it this way.
> > + spin_lock(&aunet->contid_list_lock);
> > + if (!list_empty(contid_list))
>
> We don't need the list_empty() check here do we? I think we can just
> call list_for_each_entry_rcu(), yes?
>
This is true, the list_empty check is redundant, and the for loop will fall
through if the list is empty.
> > + list_for_each_entry_rcu(cont, contid_list, list)
> > + if (cont->id == contid) {
> > + refcount_inc(&cont->refcount);
> > + goto out;
> > + }
> > + cont = kmalloc(sizeof(struct audit_contid), GFP_ATOMIC);
>
> If you had to guess, what do you think is going to be more common:
> bumping the refcount of an existing entry in the list, or adding a new
> entry? I'm asking because I always get a little nervous when doing
> allocations while holding a spinlock. Yes, you are doing it with
> GFP_ATOMIC, but it still seems like something to try and avoid if this
> is going to approach 50%. However, if the new entry is rare then the
> extra work of always doing the allocation before taking the lock and
> then freeing it afterwards might be a bad tradeoff.
>
I think this is another way of asking, will multiple processes exist in the same
network namespace? That is to say, will we expect a process to create a net
namespace, and then have other processes enter that namespace (thereby
triggering multiple calls to audit_netns_contid_add with the same net pointer
and cont id). Given that the kubernetes notion of a pod is almost by definition
multiple containers sharing a network namespace, I think the answer is that we
will be strongly biased towards the refcount_inc case, rather than the kmalloc
case. I could be wrong, but I think this is likely already in the optimized
order.
> My gut feeling says we might do about as many allocations as refcount
> bumps, but I could be thinking about this wrong.
>
> Moving the allocation outside the spinlock might also open the door to
> doing this as GFP_KERNEL, which is a good thing, but I haven't looked
> at the callers to see if that is possible (it may not be). That's an
> exercise left to the patch author (if he hasn't done that already).
>
> > + if (cont) {
> > + INIT_LIST_HEAD(&cont->list);
>
> Unless there is some guidance that INIT_LIST_HEAD() should be used
> regardless, you shouldn't need to call this here since list_add_rcu()
> will take care of any list.h related initialization.
>
There is a corner case that needs it. list_add_rcu has a check that gets
called, __list_add_valid. Its a noop in the regular case, but if
CONFIG_DEBUG_LIST is defined, its a check to ensure that the next and prev
pointers getting passed in aren't set to detectable corrupt values. If we pass
in garbage, we can get transient false positives on that check, so we need to
set the list pointers to known good values before hand, either by using kzalloc,
or INIT_LIST_HEAD, as has been done here. Given that we expressly set every
field of this structure, I think this is the right approach, as it uses the list
macro to expressly set the list values to their proper state.
> > + cont->id = contid;
> > + refcount_set(&cont->refcount, 1);
> > + list_add_rcu(&cont->list, contid_list);
> > + }
> > +out:
> > + spin_unlock(&aunet->contid_list_lock);
> > +}
>
> --
> paul moore
> www.paul-moore.com
>
^ permalink raw reply
* Re: [PATCH ghak90 V5 09/10] audit: add support for containerid to network namespaces
From: Paul Moore @ 2019-04-02 13:31 UTC (permalink / raw)
To: Neil Horman
Cc: Richard Guy Briggs, containers, linux-api,
Linux-Audit Mailing List, linux-fsdevel, LKML, netdev,
netfilter-devel, sgrubb, omosnace, dhowells, simo, Eric Paris,
Serge Hallyn, ebiederm
In-Reply-To: <20190402113150.GA17593@hmswarspite.think-freely.org>
On Tue, Apr 2, 2019 at 7:32 AM Neil Horman <nhorman@tuxdriver.com> wrote:
> On Mon, Apr 01, 2019 at 10:50:03AM -0400, Paul Moore wrote:
> > On Fri, Mar 15, 2019 at 2:35 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> > > Audit events could happen in a network namespace outside of a task
> > > context due to packets received from the net that trigger an auditing
> > > rule prior to being associated with a running task. The network
> > > namespace could be in use by multiple containers by association to the
> > > tasks in that network namespace. We still want a way to attribute
> > > these events to any potential containers. Keep a list per network
> > > namespace to track these audit container identifiiers.
> > >
> > > Add/increment the audit container identifier on:
> > > - initial setting of the audit container identifier via /proc
> > > - clone/fork call that inherits an audit container identifier
> > > - unshare call that inherits an audit container identifier
> > > - setns call that inherits an audit container identifier
> > > Delete/decrement the audit container identifier on:
> > > - an inherited audit container identifier dropped when child set
> > > - process exit
> > > - unshare call that drops a net namespace
> > > - setns call that drops a net namespace
> > >
> > > See: https://github.com/linux-audit/audit-kernel/issues/92
> > > See: https://github.com/linux-audit/audit-testsuite/issues/64
> > > See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
> > > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> > > ---
> > > include/linux/audit.h | 19 ++++++++++++
> > > kernel/audit.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
> > > kernel/nsproxy.c | 4 +++
> > > 3 files changed, 106 insertions(+), 3 deletions(-)
> >
> > ...
> >
> > > diff --git a/kernel/audit.c b/kernel/audit.c
> > > index cf448599ef34..7fa3194f5342 100644
> > > --- a/kernel/audit.c
> > > +++ b/kernel/audit.c
> > > @@ -72,6 +72,7 @@
> > > #include <linux/freezer.h>
> > > #include <linux/pid_namespace.h>
> > > #include <net/netns/generic.h>
> > > +#include <net/net_namespace.h>
> > >
> > > #include "audit.h"
> > >
> > > @@ -99,9 +100,13 @@
> > > /**
> > > * struct audit_net - audit private network namespace data
> > > * @sk: communication socket
> > > + * @contid_list: audit container identifier list
> > > + * @contid_list_lock audit container identifier list lock
> > > */
> > > struct audit_net {
> > > struct sock *sk;
> > > + struct list_head contid_list;
> > > + spinlock_t contid_list_lock;
> > > };
> > >
> > > /**
> > > @@ -275,8 +280,11 @@ struct audit_task_info init_struct_audit = {
> > > void audit_free(struct task_struct *tsk)
> > > {
> > > struct audit_task_info *info = tsk->audit;
> > > + struct nsproxy *ns = tsk->nsproxy;
> > >
> > > audit_free_syscall(tsk);
> > > + if (ns)
> > > + audit_netns_contid_del(ns->net_ns, audit_get_contid(tsk));
> > > /* Freeing the audit_task_info struct must be performed after
> > > * audit_log_exit() due to need for loginuid and sessionid.
> > > */
> > > @@ -376,6 +384,73 @@ static struct sock *audit_get_sk(const struct net *net)
> > > return aunet->sk;
> > > }
> > >
> > > +void audit_netns_contid_add(struct net *net, u64 contid)
> > > +{
> > > + struct audit_net *aunet = net_generic(net, audit_net_id);
> > > + struct list_head *contid_list = &aunet->contid_list;
> > > + struct audit_contid *cont;
> > > +
> > > + if (!audit_contid_valid(contid))
> > > + return;
> > > + if (!aunet)
> > > + return;
> >
> > We should move the contid_list assignment below this check, or decide
> > that aunet is always going to valid (?) and get rid of this check
> > completely.
> >
> I'm not sure why that would be needed. Finding the net_id list is an operation
> of a map relating net namespaces to lists, not contids to lists. We could do
> it, sure, but since they're unrelated operations, I don't think we experience
> any slowdowns from doing it this way.
In the first line of the function, when aunet is declared, it is also
assigned a value using net_generic():
struct audit_net *aunet = net_generic(net, audit_net_id);
Later in the function there is check to see if aunet is NULL, yet on
the second line of the function (before the NULL check), there is this
line of code:
struct list_head *contid_list = &aunet->contid_list;
... which could result in the dereference of a NULL pointer if aunet
is NULL. My suggestion was either to move this assignment below the
aunet-NULL check or decide that aunet was always going to be valid
(e.g. non-NULL) and do away with the aunet-NULL check completely.
Richard has since replied that the aunet-NULL check has been
demonstrated to be necessary so the proper thing to do would be to
move the assignment. I believe that is what Richard is planning on
doing.
> > > + if (cont) {
> > > + INIT_LIST_HEAD(&cont->list);
> >
> > Unless there is some guidance that INIT_LIST_HEAD() should be used
> > regardless, you shouldn't need to call this here since list_add_rcu()
> > will take care of any list.h related initialization.
>
> There is a corner case that needs it. list_add_rcu has a check that gets
> called, __list_add_valid. Its a noop in the regular case, but if
> CONFIG_DEBUG_LIST is defined, its a check to ensure that the next and prev
> pointers getting passed in aren't set to detectable corrupt values. If we pass
> in garbage, we can get transient false positives on that check, so we need to
> set the list pointers to known good values before hand, either by using kzalloc,
> or INIT_LIST_HEAD, as has been done here. Given that we expressly set every
> field of this structure, I think this is the right approach, as it uses the list
> macro to expressly set the list values to their proper state.
Good to know, thanks.
--
paul moore
www.paul-moore.com
^ permalink raw reply
* Re: [PATCH 01/17] fpga: dfl-fme-mgr: fix FME_PR_INTFC_ID register address.
From: Moritz Fischer @ 2019-04-02 13:33 UTC (permalink / raw)
To: Wu Hao; +Cc: Moritz Fischer, atull, linux-fpga, linux-kernel, linux-api
In-Reply-To: <20190402043845.GA24012@hao-dev>
Hi Wu,
On Tue, Apr 02, 2019 at 12:38:45PM +0800, Wu Hao wrote:
> On Mon, Apr 01, 2019 at 12:54:47PM -0700, Moritz Fischer wrote:
> > Hi Wu,
> >
> > On Mon, Mar 25, 2019 at 11:07:28AM +0800, Wu Hao wrote:
> > > FME_PR_INTFC_ID is used as compat_id for fpga manager and region,
> > > but high 64 bits and low 64 bits of the compat_id are swapped by
> > > mistake. This patch fixes this problem by fixing register address.
> > >
> > > Signed-off-by: Wu Hao <hao.wu@intel.com>
Acked-by: Moritz Fischer <mdf@kernel.org>
> > > ---
> > > drivers/fpga/dfl-fme-mgr.c | 4 ++--
> > > 1 file changed, 2 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> > > index 76f3770..b3f7eee 100644
> > > --- a/drivers/fpga/dfl-fme-mgr.c
> > > +++ b/drivers/fpga/dfl-fme-mgr.c
> > > @@ -30,8 +30,8 @@
> > > #define FME_PR_STS 0x10
> > > #define FME_PR_DATA 0x18
> > > #define FME_PR_ERR 0x20
> > > -#define FME_PR_INTFC_ID_H 0xA8
> > > -#define FME_PR_INTFC_ID_L 0xB0
> > > +#define FME_PR_INTFC_ID_L 0xA8
> > > +#define FME_PR_INTFC_ID_H 0xB0
> >
> > Does this handle endianess correct?
>
> Hi Moritz,
>
> This is just a bug fixing for wrong offsets given to these 2 registers
> according to spec. I think this is not endianess related, and per my
> understanding we don't need more code on endianess handling as that
> should be done inside the readq function already. :)
>
> Thanks
> Hao
Thanks for clarifying,
Moritz
^ permalink raw reply
* Re: [PATCH ghak90 V5 09/10] audit: add support for containerid to network namespaces
From: Neil Horman @ 2019-04-02 14:28 UTC (permalink / raw)
To: Paul Moore
Cc: Richard Guy Briggs, containers, linux-api,
Linux-Audit Mailing List, linux-fsdevel, LKML, netdev,
netfilter-devel, sgrubb, omosnace, dhowells, simo, Eric Paris,
Serge Hallyn, ebiederm
In-Reply-To: <CAHC9VhRW86NamkX9FcsHEYg_DJehCCAnANUqB8+gv=dxpBkd6w@mail.gmail.com>
On Tue, Apr 02, 2019 at 09:31:49AM -0400, Paul Moore wrote:
> On Tue, Apr 2, 2019 at 7:32 AM Neil Horman <nhorman@tuxdriver.com> wrote:
> > On Mon, Apr 01, 2019 at 10:50:03AM -0400, Paul Moore wrote:
> > > On Fri, Mar 15, 2019 at 2:35 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> > > > Audit events could happen in a network namespace outside of a task
> > > > context due to packets received from the net that trigger an auditing
> > > > rule prior to being associated with a running task. The network
> > > > namespace could be in use by multiple containers by association to the
> > > > tasks in that network namespace. We still want a way to attribute
> > > > these events to any potential containers. Keep a list per network
> > > > namespace to track these audit container identifiiers.
> > > >
> > > > Add/increment the audit container identifier on:
> > > > - initial setting of the audit container identifier via /proc
> > > > - clone/fork call that inherits an audit container identifier
> > > > - unshare call that inherits an audit container identifier
> > > > - setns call that inherits an audit container identifier
> > > > Delete/decrement the audit container identifier on:
> > > > - an inherited audit container identifier dropped when child set
> > > > - process exit
> > > > - unshare call that drops a net namespace
> > > > - setns call that drops a net namespace
> > > >
> > > > See: https://github.com/linux-audit/audit-kernel/issues/92
> > > > See: https://github.com/linux-audit/audit-testsuite/issues/64
> > > > See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
> > > > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> > > > ---
> > > > include/linux/audit.h | 19 ++++++++++++
> > > > kernel/audit.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
> > > > kernel/nsproxy.c | 4 +++
> > > > 3 files changed, 106 insertions(+), 3 deletions(-)
> > >
> > > ...
> > >
> > > > diff --git a/kernel/audit.c b/kernel/audit.c
> > > > index cf448599ef34..7fa3194f5342 100644
> > > > --- a/kernel/audit.c
> > > > +++ b/kernel/audit.c
> > > > @@ -72,6 +72,7 @@
> > > > #include <linux/freezer.h>
> > > > #include <linux/pid_namespace.h>
> > > > #include <net/netns/generic.h>
> > > > +#include <net/net_namespace.h>
> > > >
> > > > #include "audit.h"
> > > >
> > > > @@ -99,9 +100,13 @@
> > > > /**
> > > > * struct audit_net - audit private network namespace data
> > > > * @sk: communication socket
> > > > + * @contid_list: audit container identifier list
> > > > + * @contid_list_lock audit container identifier list lock
> > > > */
> > > > struct audit_net {
> > > > struct sock *sk;
> > > > + struct list_head contid_list;
> > > > + spinlock_t contid_list_lock;
> > > > };
> > > >
> > > > /**
> > > > @@ -275,8 +280,11 @@ struct audit_task_info init_struct_audit = {
> > > > void audit_free(struct task_struct *tsk)
> > > > {
> > > > struct audit_task_info *info = tsk->audit;
> > > > + struct nsproxy *ns = tsk->nsproxy;
> > > >
> > > > audit_free_syscall(tsk);
> > > > + if (ns)
> > > > + audit_netns_contid_del(ns->net_ns, audit_get_contid(tsk));
> > > > /* Freeing the audit_task_info struct must be performed after
> > > > * audit_log_exit() due to need for loginuid and sessionid.
> > > > */
> > > > @@ -376,6 +384,73 @@ static struct sock *audit_get_sk(const struct net *net)
> > > > return aunet->sk;
> > > > }
> > > >
> > > > +void audit_netns_contid_add(struct net *net, u64 contid)
> > > > +{
> > > > + struct audit_net *aunet = net_generic(net, audit_net_id);
> > > > + struct list_head *contid_list = &aunet->contid_list;
> > > > + struct audit_contid *cont;
> > > > +
> > > > + if (!audit_contid_valid(contid))
> > > > + return;
> > > > + if (!aunet)
> > > > + return;
> > >
> > > We should move the contid_list assignment below this check, or decide
> > > that aunet is always going to valid (?) and get rid of this check
> > > completely.
> > >
> > I'm not sure why that would be needed. Finding the net_id list is an operation
> > of a map relating net namespaces to lists, not contids to lists. We could do
> > it, sure, but since they're unrelated operations, I don't think we experience
> > any slowdowns from doing it this way.
>
> In the first line of the function, when aunet is declared, it is also
> assigned a value using net_generic():
>
> struct audit_net *aunet = net_generic(net, audit_net_id);
>
> Later in the function there is check to see if aunet is NULL, yet on
> the second line of the function (before the NULL check), there is this
> line of code:
>
> struct list_head *contid_list = &aunet->contid_list;
>
> ... which could result in the dereference of a NULL pointer if aunet
> is NULL. My suggestion was either to move this assignment below the
> aunet-NULL check or decide that aunet was always going to be valid
> (e.g. non-NULL) and do away with the aunet-NULL check completely.
> Richard has since replied that the aunet-NULL check has been
> demonstrated to be necessary so the proper thing to do would be to
> move the assignment. I believe that is what Richard is planning on
> doing.
>
oh, I'm sorry, you're right, I was looking at the contid tests not the list
tests.
Neil
> > > > + if (cont) {
> > > > + INIT_LIST_HEAD(&cont->list);
> > >
> > > Unless there is some guidance that INIT_LIST_HEAD() should be used
> > > regardless, you shouldn't need to call this here since list_add_rcu()
> > > will take care of any list.h related initialization.
> >
> > There is a corner case that needs it. list_add_rcu has a check that gets
> > called, __list_add_valid. Its a noop in the regular case, but if
> > CONFIG_DEBUG_LIST is defined, its a check to ensure that the next and prev
> > pointers getting passed in aren't set to detectable corrupt values. If we pass
> > in garbage, we can get transient false positives on that check, so we need to
> > set the list pointers to known good values before hand, either by using kzalloc,
> > or INIT_LIST_HEAD, as has been done here. Given that we expressly set every
> > field of this structure, I think this is the right approach, as it uses the list
> > macro to expressly set the list values to their proper state.
>
> Good to know, thanks.
>
> --
> paul moore
> www.paul-moore.com
>
^ permalink raw reply
* Re: [PATCHv8 00/10] Heterogenous memory node attributes
From: Greg Kroah-Hartman @ 2019-04-02 14:56 UTC (permalink / raw)
To: Keith Busch
Cc: Keith Busch, linux-kernel, linux-acpi, linux-mm, linux-api,
Rafael Wysocki, Dave Hansen, Dan Williams, Jonathan Cameron,
Brice Goglin
In-Reply-To: <20190316030407.GA1607@kroah.com>
On Fri, Mar 15, 2019 at 08:04:07PM -0700, Greg Kroah-Hartman wrote:
> On Fri, Mar 15, 2019 at 11:50:57AM -0600, Keith Busch wrote:
> > Hi Greg,
> >
> > Just wanted to check with you on how we may proceed with this series.
> > The main feature is exporting new sysfs attributes through driver core,
> > so I think it makes most sense to go through you unless you'd prefer
> > this go through a different route.
> >
> > The proposed interface has been pretty stable for a while now, and we've
> > received reviews, acks and tests on all patches. Please let me know if
> > there is anything else you'd like to see from this series, or if you
> > just need more time to get around to this.
>
> I can't do anything with patches until after -rc1 is out, sorry. Once
> that happens I'll work to dig through my pending queue and will review
> these then.
Sorry for the delay, all now queued up, thanks!
greg k-h
^ permalink raw reply
* Re: [PATCH v3 3/7] arm64: HWCAP: encapsulate elf_hwcap
From: Dave Martin @ 2019-04-02 14:58 UTC (permalink / raw)
To: Andrew Murray
Cc: Catalin Marinas, Will Deacon, Szabolcs Nagy, linux-arm-kernel,
Mark Rutland, Phil Blundell, libc-alpha, linux-api
In-Reply-To: <20190401104515.39775-4-andrew.murray@arm.com>
On Mon, Apr 01, 2019 at 11:45:11AM +0100, Andrew Murray wrote:
> The introduction of AT_HWCAP2 introduced accessors which ensure that
> hwcap features are set and tested appropriately.
>
> Let's now mandate access to elf_hwcap via these accessors by making
> elf_hwcap static within cpufeature.c.
Looks reasonable except for a couple of minor nits below.
I had wondered whether putting these accessors out of line would affect
any hot paths, but I can't see these used from anything that looks like
a hot path. So we're probably fine.
cpus_have_const_cap() is preferred for places where this matters,
anyway.
[...]
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 986ceeacd19f..84ca52fa75e5 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -35,8 +35,7 @@
> #include <asm/traps.h>
> #include <asm/virt.h>
>
> -unsigned long elf_hwcap __read_mostly;
> -EXPORT_SYMBOL_GPL(elf_hwcap);
> +static unsigned long elf_hwcap __read_mostly;
Now that this doesn't correspond directly to ELF_HWCAP any more and we
hide it, can we rename it to avoid confusion?
Maybe "kernel_hwcap"?
> #ifdef CONFIG_COMPAT
> #define COMPAT_ELF_HWCAP_DEFAULT \
> @@ -1947,6 +1946,35 @@ bool this_cpu_has_cap(unsigned int n)
> return false;
> }
>
> +void cpu_set_feature(unsigned int num)
> +{
> + WARN_ON(num >= MAX_CPU_FEATURES);
> + elf_hwcap |= BIT(num);
> +}
> +EXPORT_SYMBOL_GPL(cpu_set_feature);
> +
> +bool cpu_have_feature(unsigned int num)
> +{
> + WARN_ON(num >= MAX_CPU_FEATURES);
> + return elf_hwcap & BIT(num);
> +}
> +EXPORT_SYMBOL_GPL(cpu_have_feature);
> +
> +unsigned long cpu_get_elf_hwcap(void)
> +{
> + /*
> + * We currently only populate the first 32 bits of AT_HWCAP. Please
> + * note that for userspace compatibility we guarantee that bit 62
> + * will always be returned as 0.
> + */
Presumably also bit 63?
It is reasonable to say this here, but I think there should also be a
note in Documentation/arm64/elf_hwcaps.txt.
[...]
Cheers
---Dave
^ permalink raw reply
* Re: [PATCH v3 2/7] arm64: HWCAP: add support for AT_HWCAP2
From: Dave Martin @ 2019-04-02 14:58 UTC (permalink / raw)
To: Andrew Murray
Cc: Mark Rutland, libc-alpha, Szabolcs Nagy, Catalin Marinas,
Will Deacon, Phil Blundell, linux-api, linux-arm-kernel
In-Reply-To: <20190401104515.39775-3-andrew.murray@arm.com>
On Mon, Apr 01, 2019 at 11:45:10AM +0100, Andrew Murray wrote:
> As we will exhaust the first 32 bits of AT_HWCAP let's start
> exposing AT_HWCAP2 to userspace to give us up to 64 caps.
>
> Whilst it's possible to use the remaining 32 bits of AT_HWCAP, we
> prefer to expand into AT_HWCAP2 in order to provide a consistent
> view to userspace between ILP32 and LP64. However internal to the
> kernel we prefer to continue to use the full space of elf_hwcap.
>
> To reduce complexity and allow for future expansion, we now
> represent hwcaps in the kernel as ordinals and use a
> KERNEL_HWCAP_ prefix. This allows us to support automatic feature
> based module loading for all our hwcaps.
>
> We introduce cpu_set_feature to set hwcaps which compliments the
Nit: maybe "complements"? (I've always been a bit fuzzy on the precise
distinction, though.)
> existing cpu_have_feature helper. These helpers allow us to clean
> up existing direct uses of elf_hwcap and reduce any future effort
> required to move beyond 64 caps.
>
> For convenience we also introduce cpu_{have,set}_named_feature which
> makes use of the cpu_feature macro to allow providing a hwcap name
> without a {KERNEL_}HWCAP_ prefix.
>
> Signed-off-by: Andrew Murray <andrew.murray@arm.com>
> ---
> arch/arm64/crypto/aes-ce-ccm-glue.c | 2 +-
> arch/arm64/crypto/aes-neonbs-glue.c | 2 +-
> arch/arm64/crypto/chacha-neon-glue.c | 2 +-
> arch/arm64/crypto/crct10dif-ce-glue.c | 4 +-
> arch/arm64/crypto/ghash-ce-glue.c | 8 +--
> arch/arm64/crypto/nhpoly1305-neon-glue.c | 2 +-
> arch/arm64/crypto/sha256-glue.c | 4 +-
> arch/arm64/include/asm/cpufeature.h | 22 ++++----
> arch/arm64/include/asm/hwcap.h | 49 +++++++++++++++++-
> arch/arm64/include/uapi/asm/hwcap.h | 2 +-
> arch/arm64/kernel/cpufeature.c | 66 ++++++++++++------------
> arch/arm64/kernel/cpuinfo.c | 2 +-
> arch/arm64/kernel/fpsimd.c | 4 +-
> drivers/clocksource/arm_arch_timer.c | 8 +++
> 14 files changed, 117 insertions(+), 60 deletions(-)
>
[...]
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index e505e1fbd2b9..f06e1da1d678 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -14,15 +14,8 @@
> #include <asm/hwcap.h>
> #include <asm/sysreg.h>
>
> -/*
> - * In the arm64 world (as in the ARM world), elf_hwcap is used both internally
> - * in the kernel and for user space to keep track of which optional features
> - * are supported by the current system. So let's map feature 'x' to HWCAP_x.
> - * Note that HWCAP_x constants are bit fields so we need to take the log.
> - */
> -
> -#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
> -#define cpu_feature(x) ilog2(HWCAP_ ## x)
> +#define MAX_CPU_FEATURES 64
> +#define cpu_feature(x) (KERNEL_HWCAP_ ## x)
Nit: do we need the () here? They may be defensive, but I'm not sure
they're required.
[...]
> diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h
> index 400b80b49595..d21fe3314d90 100644
> --- a/arch/arm64/include/asm/hwcap.h
> +++ b/arch/arm64/include/asm/hwcap.h
> @@ -39,12 +39,59 @@
> #define COMPAT_HWCAP2_SHA2 (1 << 3)
> #define COMPAT_HWCAP2_CRC32 (1 << 4)
>
> +/*
> + * For userspace we represent hwcaps as a collection of HWCAP{,2}_x bitfields
> + * as described in uapi/asm/hwcap.h. For the kernel we represent hwcaps as
> + * natural numbers (in a single range of size MAX_CPU_FEATURES) defined here
> + * with prefix KERNEL_HWCAP_ mapped to their HWCAP{,2}_x counterpart.
> + *
> + * Hwcaps should be set and tested within the kernel via the
> + * cpu_{set,have}_named_feature(feature) where feature is the unique suffix
> + * of KERNEL_HWCAP_{feature}.
> + */
> +#define KERNEL_HWCAP_FP ilog2(HWCAP_FP)
> +#define KERNEL_HWCAP_ASIMD ilog2(HWCAP_ASIMD)
> +#define KERNEL_HWCAP_EVTSTRM ilog2(HWCAP_EVTSTRM)
> +#define KERNEL_HWCAP_AES ilog2(HWCAP_AES)
> +#define KERNEL_HWCAP_PMULL ilog2(HWCAP_PMULL)
> +#define KERNEL_HWCAP_SHA1 ilog2(HWCAP_SHA1)
> +#define KERNEL_HWCAP_SHA2 ilog2(HWCAP_SHA2)
> +#define KERNEL_HWCAP_CRC32 ilog2(HWCAP_CRC32)
> +#define KERNEL_HWCAP_ATOMICS ilog2(HWCAP_ATOMICS)
> +#define KERNEL_HWCAP_FPHP ilog2(HWCAP_FPHP)
> +#define KERNEL_HWCAP_ASIMDHP ilog2(HWCAP_ASIMDHP)
> +#define KERNEL_HWCAP_CPUID ilog2(HWCAP_CPUID)
> +#define KERNEL_HWCAP_ASIMDRDM ilog2(HWCAP_ASIMDRDM)
> +#define KERNEL_HWCAP_JSCVT ilog2(HWCAP_JSCVT)
> +#define KERNEL_HWCAP_FCMA ilog2(HWCAP_FCMA)
> +#define KERNEL_HWCAP_LRCPC ilog2(HWCAP_LRCPC)
> +#define KERNEL_HWCAP_DCPOP ilog2(HWCAP_DCPOP)
> +#define KERNEL_HWCAP_SHA3 ilog2(HWCAP_SHA3)
> +#define KERNEL_HWCAP_SM3 ilog2(HWCAP_SM3)
> +#define KERNEL_HWCAP_SM4 ilog2(HWCAP_SM4)
> +#define KERNEL_HWCAP_ASIMDDP ilog2(HWCAP_ASIMDDP)
> +#define KERNEL_HWCAP_SHA512 ilog2(HWCAP_SHA512)
> +#define KERNEL_HWCAP_SVE ilog2(HWCAP_SVE)
> +#define KERNEL_HWCAP_ASIMDFHM ilog2(HWCAP_ASIMDFHM)
> +#define KERNEL_HWCAP_DIT ilog2(HWCAP_DIT)
> +#define KERNEL_HWCAP_USCAT ilog2(HWCAP_USCAT)
> +#define KERNEL_HWCAP_ILRCPC ilog2(HWCAP_ILRCPC)
> +#define KERNEL_HWCAP_FLAGM ilog2(HWCAP_FLAGM)
> +#define KERNEL_HWCAP_SSBS ilog2(HWCAP_SSBS)
> +#define KERNEL_HWCAP_SB ilog2(HWCAP_SB)
> +#define KERNEL_HWCAP_PACA ilog2(HWCAP_PACA)
> +#define KERNEL_HWCAP_PACG ilog2(HWCAP_PACG)
> +#define KERNEL_HWCAP_DCPODP (ilog2(HWCAP2_DCPODP) + 32)
Nit: can we wrap this so that the "+ 32" doesn't have to be spelled out
each time?
If we are splitting ths CVADP support from this patch, then dropping
such a wrapper macro here (maybe with a comment) will serve as a
placeholder for whichever patch wins the race for the first HWCAP2
flag.
Say
#define __khwcap2_feature(x) (ilog2(HWCAP2_ ## xx) + 32)
(Optionally, we could also have __khwcap_feature() too so that
everything looks nice and regular.)
[...]
> diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
> index aa4ec53281ce..6cc8aff83805 100644
> --- a/drivers/clocksource/arm_arch_timer.c
> +++ b/drivers/clocksource/arm_arch_timer.c
> @@ -833,7 +833,11 @@ static void arch_timer_evtstrm_enable(int divider)
> cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
> | ARCH_TIMER_VIRT_EVT_EN;
> arch_timer_set_cntkctl(cntkctl);
> +#ifdef CONFIG_ARM64
> + cpu_set_named_feature(EVTSTRM);
> +#else
> elf_hwcap |= HWCAP_EVTSTRM;
> +#endif
I wonder whether we can have a generic definition for this:
#define cpu_set_named_feature(x) (elf_hwcap |= HWCAP_ ## x)
seems a reasonable fallback when the arch doesn't provide its own
version.
Although we don't have many instances, it would still be nice to avoid
ifdeffery creeping in.
[...]
We can probably pull the Documentation/arm64/elf_hwcaps.txt changes into
this patch.
It probably makes sense to pull the Documentation/arm64/elf_hwcaps.txt
updates alongside this patch in the series (or even incorporate them
into this patch, since they're not huge.)
Other than that, looks reasonable to me.
Cheers
---Dave
^ permalink raw reply
* Re: [PATCH 14/17] fpga: dfl: fme: add thermal management support
From: Moritz Fischer @ 2019-04-02 14:59 UTC (permalink / raw)
To: Wu Hao
Cc: atull, mdf, linux-fpga, linux-kernel, linux-api, Luwei Kang,
Russ Weight, Xu Yilun
In-Reply-To: <1553483264-5379-15-git-send-email-hao.wu@intel.com>
Hi Wu,
On Mon, Mar 25, 2019 at 11:07:41AM +0800, Wu Hao wrote:
> This patch adds support to thermal management private feature for DFL
> FPGA Management Engine (FME). As thermal throttling is handled by
> hardware automatically per pre-defined thresholds, this private
> feature driver only provides read-only sysfs interfaces for user
> to read temperature, thresholds, threshold policy and other info.
>
> Signed-off-by: Luwei Kang <luwei.kang@intel.com>
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> Signed-off-by: Xu Yilun <yilun.xu@intel.com>
> Signed-off-by: Wu Hao <hao.wu@intel.com>
> ---
> Documentation/ABI/testing/sysfs-platform-dfl-fme | 56 +++++++
> drivers/fpga/dfl-fme-main.c | 202 +++++++++++++++++++++++
> 2 files changed, 258 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> index b8327e9..d3aeb88 100644
> --- a/Documentation/ABI/testing/sysfs-platform-dfl-fme
> +++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme
> @@ -44,3 +44,59 @@ Description: Read-only. It returns socket_id to indicate which socket
> this FPGA belongs to, only valid for integrated solution.
> User only needs this information, in case standard numa node
> can't provide correct information.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/temperature
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns temperature (in Celsius) of this FPGA
> + device.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/threshold1
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. Read this file to get the temperature threshold1
> + (in Celsius).
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/threshold2
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. Read this file to get the temperature threshold2
> + (in Celsius).
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/trip_threshold
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns trip threshold (in Celsius), once FPGA
> + temperature reaches trip threshold, it triggers a fatal event
> + to board management controller (BMC) to shutdown FPGA.
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/threshold1_status
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if temperature reaches threshold1,
> + otherwise 0. Once temperature reaches threshold1, hardware
> + will automatically enter throttling state (AP1 - 50%
> + or AP2 - 90% throttling, see 'threshold1_policy').
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/threshold2_status
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. It returns 1 if temperature reaches threshold2,
> + otherwise 0. Once temperature reaches threshold2, hardware
> + will automatically enter the deepest throttling state (AP6
> + - 100% throttling).
> +
> +What: /sys/bus/platform/devices/dfl-fme.0/thermal_mgmt/threshold1_policy
> +Date: March 2019
> +KernelVersion: 5.2
> +Contact: Wu Hao <hao.wu@intel.com>
> +Description: Read-only. Read this file to get the policy of temperature
> + threshold1. It only supports two value (policy):
> + 0 - AP2 state (90% throttling)
> + 1 - AP1 state (50% throttling)
These look like they could directly map to the linux thermal framework,
any reason you can't use the thermal framework?
The trip stuff literally maps 1:1 to what a thermal driver does, I think
that's something you'd wanna consider.
Cheers,
Moritz
^ permalink raw reply
* Re: [PATCH v3 6/7] arm64: Advertise ARM64_HAS_DCPODP cpu feature
From: Dave Martin @ 2019-04-02 14:59 UTC (permalink / raw)
To: Andrew Murray
Cc: Mark Rutland, libc-alpha, Szabolcs Nagy, Catalin Marinas,
Will Deacon, Phil Blundell, linux-api, linux-arm-kernel
In-Reply-To: <20190401104515.39775-7-andrew.murray@arm.com>
On Mon, Apr 01, 2019 at 11:45:14AM +0100, Andrew Murray wrote:
> Advertise ARM64_HAS_DCPOP when both DC CVAP and DC CVADP are supported.
Do you mean ARM64_HAS_DCPODP?
And do we need this? This capability flag doesn't currently appear to
be used for anything (which makes me wonder whether it _should_ be wired
up to something in the kernel).
Do we expect the kernel to do something special with this in the future?
OTOH, we get a nice printk when the feature is detected, and the code
size cost is insignificant. So, if there's a reasonable expectation that
we will use it someday, I don't see a big problem with having it.
Cheers
---Dave
>
> Signed-off-by: Andrew Murray <andrew.murray@arm.com>
> ---
> arch/arm64/include/asm/cpucaps.h | 3 ++-
> arch/arm64/kernel/cpufeature.c | 9 +++++++++
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
> index f6a76e43f39e..defdc67d9ab4 100644
> --- a/arch/arm64/include/asm/cpucaps.h
> +++ b/arch/arm64/include/asm/cpucaps.h
> @@ -61,7 +61,8 @@
> #define ARM64_HAS_GENERIC_AUTH_ARCH 40
> #define ARM64_HAS_GENERIC_AUTH_IMP_DEF 41
> #define ARM64_HAS_IRQ_PRIO_MASKING 42
> +#define ARM64_HAS_DCPODP 43
>
> -#define ARM64_NCAPS 43
> +#define ARM64_NCAPS 44
>
> #endif /* __ASM_CPUCAPS_H */
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 5e27d2dbe45e..c74b25895c43 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -1339,6 +1339,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
> .field_pos = ID_AA64ISAR1_DPB_SHIFT,
> .min_field_value = 1,
> },
> + {
> + .desc = "Data cache clean to Point of Deep Persistence",
> + .capability = ARM64_HAS_DCPODP,
> + .type = ARM64_CPUCAP_SYSTEM_FEATURE,
> + .matches = has_cpuid_feature,
> + .sys_reg = SYS_ID_AA64ISAR1_EL1,
> + .field_pos = ID_AA64ISAR1_DPB_SHIFT,
> + .min_field_value = 2,
> + },
> #endif
> #ifdef CONFIG_ARM64_SVE
> {
> --
> 2.21.0
>
^ permalink raw reply
* Re: [PATCH v3 3/7] arm64: HWCAP: encapsulate elf_hwcap
From: Andrew Murray @ 2019-04-02 15:06 UTC (permalink / raw)
To: Dave Martin
Cc: Mark Rutland, libc-alpha, Szabolcs Nagy, Catalin Marinas,
Will Deacon, Phil Blundell, linux-api, linux-arm-kernel
In-Reply-To: <20190402145821.GH3567@e103592.cambridge.arm.com>
On Tue, Apr 02, 2019 at 03:58:21PM +0100, Dave Martin wrote:
> On Mon, Apr 01, 2019 at 11:45:11AM +0100, Andrew Murray wrote:
> > The introduction of AT_HWCAP2 introduced accessors which ensure that
> > hwcap features are set and tested appropriately.
> >
> > Let's now mandate access to elf_hwcap via these accessors by making
> > elf_hwcap static within cpufeature.c.
>
> Looks reasonable except for a couple of minor nits below.
>
> I had wondered whether putting these accessors out of line would affect
> any hot paths, but I can't see these used from anything that looks like
> a hot path. So we're probably fine.
>
> cpus_have_const_cap() is preferred for places where this matters,
> anyway.
>
> [...]
>
> > diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> > index 986ceeacd19f..84ca52fa75e5 100644
> > --- a/arch/arm64/kernel/cpufeature.c
> > +++ b/arch/arm64/kernel/cpufeature.c
> > @@ -35,8 +35,7 @@
> > #include <asm/traps.h>
> > #include <asm/virt.h>
> >
> > -unsigned long elf_hwcap __read_mostly;
> > -EXPORT_SYMBOL_GPL(elf_hwcap);
> > +static unsigned long elf_hwcap __read_mostly;
>
> Now that this doesn't correspond directly to ELF_HWCAP any more and we
> hide it, can we rename it to avoid confusion?
>
> Maybe "kernel_hwcap"?
Yes this seems reasonable.
>
> > #ifdef CONFIG_COMPAT
> > #define COMPAT_ELF_HWCAP_DEFAULT \
> > @@ -1947,6 +1946,35 @@ bool this_cpu_has_cap(unsigned int n)
> > return false;
> > }
> >
> > +void cpu_set_feature(unsigned int num)
> > +{
> > + WARN_ON(num >= MAX_CPU_FEATURES);
> > + elf_hwcap |= BIT(num);
> > +}
> > +EXPORT_SYMBOL_GPL(cpu_set_feature);
> > +
> > +bool cpu_have_feature(unsigned int num)
> > +{
> > + WARN_ON(num >= MAX_CPU_FEATURES);
> > + return elf_hwcap & BIT(num);
> > +}
> > +EXPORT_SYMBOL_GPL(cpu_have_feature);
> > +
> > +unsigned long cpu_get_elf_hwcap(void)
> > +{
> > + /*
> > + * We currently only populate the first 32 bits of AT_HWCAP. Please
> > + * note that for userspace compatibility we guarantee that bit 62
> > + * will always be returned as 0.
> > + */
>
> Presumably also bit 63?
Yes, I will add this too.
>
> It is reasonable to say this here, but I think there should also be a
> note in Documentation/arm64/elf_hwcaps.txt.
This is already present in this series, I'll update it to reflect bit 63
also.
Thanks,
Andrew Murray
>
> [...]
>
> Cheers
> ---Dave
^ permalink raw reply
* Re: [PATCH 12/17] fpga: dfl: afu: add STP (SignalTap) support
From: Moritz Fischer @ 2019-04-02 15:07 UTC (permalink / raw)
To: Wu Hao; +Cc: atull, mdf, linux-fpga, linux-kernel, linux-api, Xu Yilun
In-Reply-To: <1553483264-5379-13-git-send-email-hao.wu@intel.com>
Hi Wu,
On Mon, Mar 25, 2019 at 11:07:39AM +0800, Wu Hao wrote:
> STP (SignalTap) is one of the private features under the port for
> debugging. This patch adds private feature driver support for it
> to allow userspace applications to mmap related mmio region and
> provide STP service.
>
> Signed-off-by: Xu Yilun <yilun.xu@intel.com>
> Signed-off-by: Wu Hao <hao.wu@intel.com>
Acked-by: Moritz Fischer <mdf@kernel.org>
> ---
> drivers/fpga/dfl-afu-main.c | 34 ++++++++++++++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
>
> diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
> index 754729e..14970a4 100644
> --- a/drivers/fpga/dfl-afu-main.c
> +++ b/drivers/fpga/dfl-afu-main.c
> @@ -518,6 +518,36 @@ static const struct dfl_feature_ops port_afu_ops = {
> .uinit = port_afu_uinit,
> };
>
> +static int port_stp_init(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + struct resource *res = &pdev->resource[feature->resource_index];
> +
> + dev_dbg(&pdev->dev, "PORT STP Init.\n");
> +
> + return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
> + DFL_PORT_REGION_INDEX_STP,
> + resource_size(res), res->start,
> + DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
> + DFL_PORT_REGION_WRITE);
> +}
> +
> +static void port_stp_uinit(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + dev_dbg(&pdev->dev, "PORT STP UInit.\n");
> +}
> +
> +static const struct dfl_feature_id port_stp_id_table[] = {
> + {.id = PORT_FEATURE_ID_STP,},
> + {0,}
> +};
> +
> +static const struct dfl_feature_ops port_stp_ops = {
> + .init = port_stp_init,
> + .uinit = port_stp_uinit,
> +};
> +
> static struct dfl_feature_driver port_feature_drvs[] = {
> {
> .id_table = port_hdr_id_table,
> @@ -532,6 +562,10 @@ static struct dfl_feature_driver port_feature_drvs[] = {
> .ops = &port_err_ops,
> },
> {
> + .id_table = port_stp_id_table,
> + .ops = &port_stp_ops,
> + },
> + {
> .ops = NULL,
> }
> };
> --
> 2.7.4
>
Thanks,
Moritz
^ permalink raw reply
* Re: [PATCH 09/17] fpga: dfl: add id_table for dfl private feature driver
From: Moritz Fischer @ 2019-04-02 15:09 UTC (permalink / raw)
To: Wu Hao; +Cc: atull, mdf, linux-fpga, linux-kernel, linux-api, Xu Yilun
In-Reply-To: <1553483264-5379-10-git-send-email-hao.wu@intel.com>
Hi Wu,
On Mon, Mar 25, 2019 at 11:07:36AM +0800, Wu Hao wrote:
> This patch adds id_table for each dfl private feature driver,
> it allows to reuse same private feature driver to match and support
> multiple dfl private features.
>
> Signed-off-by: Xu Yilun <yilun.xu@intel.com>
> Signed-off-by: Wu Hao <hao.wu@intel.com>
Acked-by: Moritz Fischer <mdf@kernel.org>
> ---
> drivers/fpga/dfl-afu-main.c | 14 ++++++++++++--
> drivers/fpga/dfl-fme-main.c | 11 ++++++++---
> drivers/fpga/dfl-fme-pr.c | 7 ++++++-
> drivers/fpga/dfl-fme.h | 3 ++-
> drivers/fpga/dfl.c | 21 +++++++++++++++++++--
> drivers/fpga/dfl.h | 21 +++++++++++++++------
> 6 files changed, 62 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
> index 82fd80a..2916876 100644
> --- a/drivers/fpga/dfl-afu-main.c
> +++ b/drivers/fpga/dfl-afu-main.c
> @@ -440,6 +440,11 @@ port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
> return ret;
> }
>
> +static const struct dfl_feature_id port_hdr_id_table[] = {
> + {.id = PORT_FEATURE_ID_HEADER,},
> + {0,}
> +};
> +
> static const struct dfl_feature_ops port_hdr_ops = {
> .init = port_hdr_init,
> .uinit = port_hdr_uinit,
> @@ -500,6 +505,11 @@ static void port_afu_uinit(struct platform_device *pdev,
> sysfs_remove_files(&pdev->dev.kobj, port_afu_attrs);
> }
>
> +static const struct dfl_feature_id port_afu_id_table[] = {
> + {.id = PORT_FEATURE_ID_AFU,},
> + {0,}
> +};
> +
> static const struct dfl_feature_ops port_afu_ops = {
> .init = port_afu_init,
> .uinit = port_afu_uinit,
> @@ -507,11 +517,11 @@ static const struct dfl_feature_ops port_afu_ops = {
>
> static struct dfl_feature_driver port_feature_drvs[] = {
> {
> - .id = PORT_FEATURE_ID_HEADER,
> + .id_table = port_hdr_id_table,
> .ops = &port_hdr_ops,
> },
> {
> - .id = PORT_FEATURE_ID_AFU,
> + .id_table = port_afu_id_table,
> .ops = &port_afu_ops,
> },
> {
> diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
> index 8b2a337..38c6342 100644
> --- a/drivers/fpga/dfl-fme-main.c
> +++ b/drivers/fpga/dfl-fme-main.c
> @@ -158,6 +158,11 @@ static long fme_hdr_ioctl(struct platform_device *pdev,
> return -ENODEV;
> }
>
> +static const struct dfl_feature_id fme_hdr_id_table[] = {
> + {.id = FME_FEATURE_ID_HEADER,},
> + {0,}
> +};
> +
> static const struct dfl_feature_ops fme_hdr_ops = {
> .init = fme_hdr_init,
> .uinit = fme_hdr_uinit,
> @@ -166,12 +171,12 @@ static const struct dfl_feature_ops fme_hdr_ops = {
>
> static struct dfl_feature_driver fme_feature_drvs[] = {
> {
> - .id = FME_FEATURE_ID_HEADER,
> + .id_table = fme_hdr_id_table,
> .ops = &fme_hdr_ops,
> },
> {
> - .id = FME_FEATURE_ID_PR_MGMT,
> - .ops = &pr_mgmt_ops,
> + .id_table = fme_pr_mgmt_id_table,
> + .ops = &fme_pr_mgmt_ops,
> },
> {
> .ops = NULL,
> diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
> index 8a0e46a..b054ac6 100644
> --- a/drivers/fpga/dfl-fme-pr.c
> +++ b/drivers/fpga/dfl-fme-pr.c
> @@ -482,7 +482,12 @@ static long fme_pr_ioctl(struct platform_device *pdev,
> return ret;
> }
>
> -const struct dfl_feature_ops pr_mgmt_ops = {
> +const struct dfl_feature_id fme_pr_mgmt_id_table[] = {
> + {.id = FME_FEATURE_ID_PR_MGMT,},
> + {0}
> +};
> +
> +const struct dfl_feature_ops fme_pr_mgmt_ops = {
> .init = pr_mgmt_init,
> .uinit = pr_mgmt_uinit,
> .ioctl = fme_pr_ioctl,
> diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h
> index de20755..7a021c4 100644
> --- a/drivers/fpga/dfl-fme.h
> +++ b/drivers/fpga/dfl-fme.h
> @@ -35,6 +35,7 @@ struct dfl_fme {
> struct dfl_feature_platform_data *pdata;
> };
>
> -extern const struct dfl_feature_ops pr_mgmt_ops;
> +extern const struct dfl_feature_ops fme_pr_mgmt_ops;
> +extern const struct dfl_feature_id fme_pr_mgmt_id_table[];
>
> #endif /* __DFL_FME_H */
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> index c5aa287..65f91ef 100644
> --- a/drivers/fpga/dfl.c
> +++ b/drivers/fpga/dfl.c
> @@ -14,6 +14,8 @@
>
> #include "dfl.h"
>
> +#define DRV_VERSION "0.8"
> +
> static DEFINE_MUTEX(dfl_id_mutex);
>
> /*
> @@ -274,6 +276,21 @@ static int dfl_feature_instance_init(struct platform_device *pdev,
> return ret;
> }
>
> +static bool dfl_feature_drv_match(struct dfl_feature *feature,
> + struct dfl_feature_driver *driver)
> +{
> + const struct dfl_feature_id *ids = driver->id_table;
> +
> + if (ids) {
> + while (ids->id) {
> + if (ids->id == feature->id)
> + return true;
> + ids++;
> + }
> + }
> + return false;
> +}
> +
> /**
> * dfl_fpga_dev_feature_init - init for sub features of dfl feature device
> * @pdev: feature device.
> @@ -294,8 +311,7 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev,
>
> while (drv->ops) {
> dfl_fpga_dev_for_each_feature(pdata, feature) {
> - /* match feature and drv using id */
> - if (feature->id == drv->id) {
> + if (dfl_feature_drv_match(feature, drv)) {
> ret = dfl_feature_instance_init(pdev, pdata,
> feature, drv);
> if (ret)
> @@ -1164,3 +1180,4 @@ module_exit(dfl_fpga_exit);
> MODULE_DESCRIPTION("FPGA Device Feature List (DFL) Support");
> MODULE_AUTHOR("Intel Corporation");
> MODULE_LICENSE("GPL v2");
> +MODULE_VERSION(DRV_VERSION);
> diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> index 3c5dc3a..fbc57f0 100644
> --- a/drivers/fpga/dfl.h
> +++ b/drivers/fpga/dfl.h
> @@ -30,8 +30,8 @@
> /* plus one for fme device */
> #define MAX_DFL_FEATURE_DEV_NUM (MAX_DFL_FPGA_PORT_NUM + 1)
>
> -/* Reserved 0x0 for Header Group Register and 0xff for AFU */
> -#define FEATURE_ID_FIU_HEADER 0x0
> +/* Reserved 0xfe for Header Group Register and 0xff for AFU */
> +#define FEATURE_ID_FIU_HEADER 0xfe
> #define FEATURE_ID_AFU 0xff
>
> #define FME_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER
> @@ -169,13 +169,22 @@ void dfl_fpga_port_ops_put(struct dfl_fpga_port_ops *ops);
> int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id);
>
> /**
> - * struct dfl_feature_driver - sub feature's driver
> + * struct dfl_feature_id - dfl private feature id
> *
> - * @id: sub feature id.
> - * @ops: ops of this sub feature.
> + * @id: unique dfl private feature id.
> */
> -struct dfl_feature_driver {
> +struct dfl_feature_id {
> u64 id;
> +};
> +
> +/**
> + * struct dfl_feature_driver - dfl private feature driver
> + *
> + * @id_table: id_table for dfl private features supported by this driver.
> + * @ops: ops of this dfl private feature driver.
> + */
> +struct dfl_feature_driver {
> + const struct dfl_feature_id *id_table;
> const struct dfl_feature_ops *ops;
> };
>
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH v3 3/7] arm64: HWCAP: encapsulate elf_hwcap
From: Suzuki K Poulose @ 2019-04-02 15:32 UTC (permalink / raw)
To: andrew.murray, dave.martin
Cc: mark.rutland, libc-alpha, Szabolcs.Nagy, catalin.marinas,
will.deacon, pb, linux-api, linux-arm-kernel
In-Reply-To: <20190402150654.GD53702@e119886-lin.cambridge.arm.com>
Hi,
On 02/04/2019 16:06, Andrew Murray wrote:
> On Tue, Apr 02, 2019 at 03:58:21PM +0100, Dave Martin wrote:
>> On Mon, Apr 01, 2019 at 11:45:11AM +0100, Andrew Murray wrote:
>>> The introduction of AT_HWCAP2 introduced accessors which ensure that
>>> hwcap features are set and tested appropriately.
>>>
>>> Let's now mandate access to elf_hwcap via these accessors by making
>>> elf_hwcap static within cpufeature.c.
>>
>> Looks reasonable except for a couple of minor nits below.
>>
>> I had wondered whether putting these accessors out of line would affect
>> any hot paths, but I can't see these used from anything that looks like
>> a hot path. So we're probably fine.
>>
>> cpus_have_const_cap() is preferred for places where this matters,
>> anyway.
Btw, thats for cpu_hwcaps, which is completely different from elf_hwcaps.
>>
>> [...]
>>
>>> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
>>> index 986ceeacd19f..84ca52fa75e5 100644
>>> --- a/arch/arm64/kernel/cpufeature.c
>>> +++ b/arch/arm64/kernel/cpufeature.c
>>> @@ -35,8 +35,7 @@
>>> #include <asm/traps.h>
>>> #include <asm/virt.h>
>>>
>>> -unsigned long elf_hwcap __read_mostly;
>>> -EXPORT_SYMBOL_GPL(elf_hwcap);
>>> +static unsigned long elf_hwcap __read_mostly;
>>
>> Now that this doesn't correspond directly to ELF_HWCAP any more and we
>> hide it, can we rename it to avoid confusion?
>>
>> Maybe "kernel_hwcap"?
>
> Yes this seems reasonable.
nit:
As mentioned above we have "cpu_hwcaps" for the features only internally
by the kernel. Naming it "kernel_hwcap" kind of looses the hint that the
major purpose is for userspace consumption and could easily confuse with
the poorly named "cpu_hwcaps" which should have been called kernel_hwcaps.
How about "user_hwcaps" ? Or preferrably something closer to that.
Cheers
Suzuki
^ permalink raw reply
* [PATCH v4 00/17] fscrypt: key management improvements
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
Hello,
This patchset makes major improvements to how keys are added, removed,
and derived in fscrypt, aka ext4/f2fs/ubifs encryption. It does this by
adding new ioctls that add and remove encryption keys directly to/from
the filesystem, and by adding a new encryption policy version ("v2")
where the user-provided keys are only used as input to HKDF-SHA512 and
are identified by their cryptographic hash.
All new APIs and all cryptosystem changes are documented in
Documentation/filesystems/fscrypt.rst. Userspace can use the new key
management ioctls with existing encrypted directories, but migrating to
v2 encryption policies is needed for the full benefits.
These changes solve four interrelated problems:
(1) Providing fscrypt keys via process-subscribed keyrings is abusing
encryption as an OS-level access control mechanism, causing many
bugs where processes don't get access to the keys they need -- e.g.,
when a 'sudo' command or a system service needs to access encrypted
files. It's also inconsistent with the filesystem/VFS "view" of
encrypted files which is global, so sometimes things randomly happen
to work anyway due to caching. Regardless, currently almost all
fscrypt users actually do need global keys, so they're having to use
workarounds that heavily abuse the session or user keyrings, e.g.
Android and Chromium OS both use a systemwide "session keyring" and
the 'fscrypt' tool links all user keyrings into root's user keyring.
(2) Currently there's no way to securely and efficiently remove a
fscrypt key such that not only is the original key wiped, but also
all files and directories protected by that key are "locked" and
their per-file keys wiped. Many users want this and are using
'echo 2 > /proc/sys/vm/drop_caches' as a workaround, but this is
root-only, and also is overkill so can be a performance disaster.
(3) The key derivation function (KDF) that fscrypt uses to derive
per-file keys is nonstandard, inflexible, and has some weaknesses
such as being reversible and not evenly distributing the entropy
from the user-provided keys.
(4) fscrypt doesn't check that the correct key was supplied. This can
be a security vulnerability, since it allows malicious local users
to associate the wrong key with files to which they have read-only
access, causing other users' processes to read/write the wrong data.
Ultimately, the solutions to these problems all tie into each other. By
adding a filesystem-level encryption keyring with ioctls to add/remove
keys to/from it, the keys are made usable filesystem-wide (solves
problem #1). It also becomes easy to track the inodes that were
"unlocked" with each key, so they can be evicted when the key is removed
(solves problem #2). Moreover, the filesystem-level keyring is a
natural place to store an HMAC transform keyed by each key, thus making
it easy and efficient to switch the KDF to HKDF (solves problem #3).
Finally, to check that the correct key was supplied, I use HKDF to
derive a cryptographically secure key_identifier for each key (solves
problem #4). This in combination with key quotas and other careful
precautions also makes it safe to allow non-root users to add and remove
keys to/from the filesystem-level keyring. Thus, all problems are
solved without having to restrict the fscrypt API to root only.
The patchset is organized as follows:
- Patches 1-9 add new ioctls FS_IOC_ADD_ENCRYPTION_KEY,
FS_IOC_REMOVE_ENCRYPTION_KEY, and FS_IOC_GET_ENCRYPTION_KEY_STATUS.
Adding a key logically "unlocks" all files on the filesystem that are
protected by that key; removing a key "locks" them again.
- Patches 10-13 add support for v2 encryption policies.
- Patches 14-16 wire up the new ioctls to ext4, f2fs, and ubifs.
- Patch 17 updates the fscrypt documentation for all the changes.
Changes v3 => v4:
- Introduce fscrypt_sb_free() to avoid an extra #ifdef.
- Fix UBIFS's ->drop_inode().
- Add 'version' to union fscrypt_policy and union fscrypt_context.
Changes v2 => v3:
- Use ->drop_inode() to trigger the inode eviction during/after
FS_IOC_REMOVE_ENCRYPTION_KEY, as suggested by Dave Chinner.
- A few small cleanups.
v1 of this patchset was sent in October 2017 with title "fscrypt:
filesystem-level keyring and v2 policy support". This revived version
follows the same basic design but incorporates numerous improvements,
such as splitting keyinfo.c into multiple files for much better
understandability, and introducing "per-mode" encryption keys to
implement the semantics of the DIRECT_KEY encryption policy flag.
This applies to v5.1-rc3. You can also get it from git at:
Repository: https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git
Branch: fscrypt-key-mgmt-improvements-v4
I've started writing xfstests for the new APIs. So far they cover basic
functionality. They can be found at:
Repository: https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/xfstests-dev.git
Branch: fscrypt-key-mgmt-improvements
The xfstests depend on new xfs_io commands which can be found at:
Repository: https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/xfsprogs-dev.git
Branch: fscrypt-key-mgmt-improvements
I've also made proof-of-concept changes to the 'fscrypt' userspace
program (https://github.com/google/fscrypt) to make it support v2
encryption policies. You can find these changes in git at:
Repository: https://github.com/ebiggers/fscrypt.git
Branch: fscrypt-key-mgmt-improvements
To make the 'fscrypt' userspace program experimentally use v2 encryption
policies on new encrypted directories, add the following to
/etc/fscrypt.conf within the "options" section:
"policy_version": "2"
It's also planned for Android and Chromium OS to switch to the new
ioctls and eventually to v2 encryption policies. Work-in-progress,
proof-of-concept changes by Satya Tangirala for AOSP can be found at
https://android-review.googlesource.com/q/topic:fscrypt-key-mgmt-improvements
Eric Biggers (17):
fs, fscrypt: move uapi definitions to new header <linux/fscrypt.h>
fscrypt: use FSCRYPT_ prefix for uapi constants
fscrypt: use FSCRYPT_* definitions, not FS_*
fscrypt: add ->ci_inode to fscrypt_info
fscrypt: refactor v1 policy key setup into keysetup_legacy.c
fscrypt: add FS_IOC_ADD_ENCRYPTION_KEY ioctl
fs/dcache.c: add shrink_dcache_inode()
fscrypt: add FS_IOC_REMOVE_ENCRYPTION_KEY ioctl
fscrypt: add FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl
fscrypt: add an HKDF-SHA512 implementation
fscrypt: v2 encryption policy support
fscrypt: allow unprivileged users to add/remove keys for v2 policies
fscrypt: require that key be added when setting a v2 encryption policy
ext4: wire up new fscrypt ioctls
f2fs: wire up new fscrypt ioctls
ubifs: wire up new fscrypt ioctls
fscrypt: document the new ioctls and policy version
Documentation/filesystems/fscrypt.rst | 670 +++++++++++++++----
MAINTAINERS | 1 +
fs/crypto/Kconfig | 2 +
fs/crypto/Makefile | 10 +-
fs/crypto/crypto.c | 14 +-
fs/crypto/fname.c | 5 +-
fs/crypto/fscrypt_private.h | 366 ++++++++--
fs/crypto/hkdf.c | 188 ++++++
fs/crypto/keyinfo.c | 592 -----------------
fs/crypto/keyring.c | 917 ++++++++++++++++++++++++++
fs/crypto/keysetup.c | 540 +++++++++++++++
fs/crypto/keysetup_legacy.c | 340 ++++++++++
fs/crypto/policy.c | 388 ++++++++---
fs/dcache.c | 32 +
fs/ext4/ioctl.c | 24 +
fs/ext4/super.c | 3 +
fs/f2fs/file.c | 46 ++
fs/f2fs/super.c | 2 +
fs/super.c | 2 +
fs/ubifs/ioctl.c | 23 +-
fs/ubifs/super.c | 11 +
include/linux/dcache.h | 1 +
include/linux/fs.h | 1 +
include/linux/fscrypt.h | 49 +-
include/uapi/linux/fs.h | 54 +-
include/uapi/linux/fscrypt.h | 163 +++++
26 files changed, 3516 insertions(+), 928 deletions(-)
create mode 100644 fs/crypto/hkdf.c
delete mode 100644 fs/crypto/keyinfo.c
create mode 100644 fs/crypto/keyring.c
create mode 100644 fs/crypto/keysetup.c
create mode 100644 fs/crypto/keysetup_legacy.c
create mode 100644 include/uapi/linux/fscrypt.h
--
2.21.0
^ permalink raw reply
* [PATCH v4 01/17] fs, fscrypt: move uapi definitions to new header <linux/fscrypt.h>
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
More fscrypt definitions are being added, and we shouldn't use a
disproportionate amount of space in <linux/fs.h> for fscrypt stuff.
So move the fscrypt definitions to a new header <linux/fscrypt.h>.
For source compatibility with existing userspace programs, <linux/fs.h>
still includes the new header.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
MAINTAINERS | 1 +
include/linux/fscrypt.h | 1 +
include/uapi/linux/fs.h | 54 ++-------------------------------
include/uapi/linux/fscrypt.h | 58 ++++++++++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 51 deletions(-)
create mode 100644 include/uapi/linux/fscrypt.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 43b36dbed48e7..3b84bf3bf7cae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6351,6 +6351,7 @@ T: git git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git
S: Supported
F: fs/crypto/
F: include/linux/fscrypt*.h
+F: include/uapi/linux/fscrypt.h
F: Documentation/filesystems/fscrypt.rst
FSI-ATTACHED I2C DRIVER
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index e5194fc3983e9..8b1e444214973 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <uapi/linux/fscrypt.h>
#define FS_CRYPTO_BLOCK_SIZE 16
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 121e82ce296b5..0f4e66b1bcd09 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -13,6 +13,9 @@
#include <linux/limits.h>
#include <linux/ioctl.h>
#include <linux/types.h>
+#ifndef __KERNEL__
+#include <linux/fscrypt.h>
+#endif
/* Use of MS_* flags within the kernel is restricted to core mount(2) code. */
#if !defined(__KERNEL__)
@@ -212,57 +215,6 @@ struct fsxattr {
#define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX])
#define FS_IOC_SETFSLABEL _IOW(0x94, 50, char[FSLABEL_MAX])
-/*
- * File system encryption support
- */
-/* Policy provided via an ioctl on the topmost directory */
-#define FS_KEY_DESCRIPTOR_SIZE 8
-
-#define FS_POLICY_FLAGS_PAD_4 0x00
-#define FS_POLICY_FLAGS_PAD_8 0x01
-#define FS_POLICY_FLAGS_PAD_16 0x02
-#define FS_POLICY_FLAGS_PAD_32 0x03
-#define FS_POLICY_FLAGS_PAD_MASK 0x03
-#define FS_POLICY_FLAG_DIRECT_KEY 0x04 /* use master key directly */
-#define FS_POLICY_FLAGS_VALID 0x07
-
-/* Encryption algorithms */
-#define FS_ENCRYPTION_MODE_INVALID 0
-#define FS_ENCRYPTION_MODE_AES_256_XTS 1
-#define FS_ENCRYPTION_MODE_AES_256_GCM 2
-#define FS_ENCRYPTION_MODE_AES_256_CBC 3
-#define FS_ENCRYPTION_MODE_AES_256_CTS 4
-#define FS_ENCRYPTION_MODE_AES_128_CBC 5
-#define FS_ENCRYPTION_MODE_AES_128_CTS 6
-#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* Removed, do not use. */
-#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* Removed, do not use. */
-#define FS_ENCRYPTION_MODE_ADIANTUM 9
-
-struct fscrypt_policy {
- __u8 version;
- __u8 contents_encryption_mode;
- __u8 filenames_encryption_mode;
- __u8 flags;
- __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
-};
-
-#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
-#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
-#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
-
-/* Parameters for passing an encryption key into the kernel keyring */
-#define FS_KEY_DESC_PREFIX "fscrypt:"
-#define FS_KEY_DESC_PREFIX_SIZE 8
-
-/* Structure that userspace passes to the kernel keyring */
-#define FS_MAX_KEY_SIZE 64
-
-struct fscrypt_key {
- __u32 mode;
- __u8 raw[FS_MAX_KEY_SIZE];
- __u32 size;
-};
-
/*
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
*
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
new file mode 100644
index 0000000000000..193339cb53fdb
--- /dev/null
+++ b/include/uapi/linux/fscrypt.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_FSCRYPT_H
+#define _UAPI_LINUX_FSCRYPT_H
+
+#include <linux/types.h>
+
+/*
+ * File system encryption support
+ */
+/* Policy provided via an ioctl on the topmost directory */
+#define FS_KEY_DESCRIPTOR_SIZE 8
+
+#define FS_POLICY_FLAGS_PAD_4 0x00
+#define FS_POLICY_FLAGS_PAD_8 0x01
+#define FS_POLICY_FLAGS_PAD_16 0x02
+#define FS_POLICY_FLAGS_PAD_32 0x03
+#define FS_POLICY_FLAGS_PAD_MASK 0x03
+#define FS_POLICY_FLAG_DIRECT_KEY 0x04 /* use master key directly */
+#define FS_POLICY_FLAGS_VALID 0x07
+
+/* Encryption algorithms */
+#define FS_ENCRYPTION_MODE_INVALID 0
+#define FS_ENCRYPTION_MODE_AES_256_XTS 1
+#define FS_ENCRYPTION_MODE_AES_256_GCM 2
+#define FS_ENCRYPTION_MODE_AES_256_CBC 3
+#define FS_ENCRYPTION_MODE_AES_256_CTS 4
+#define FS_ENCRYPTION_MODE_AES_128_CBC 5
+#define FS_ENCRYPTION_MODE_AES_128_CTS 6
+#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* Removed, do not use. */
+#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* Removed, do not use. */
+#define FS_ENCRYPTION_MODE_ADIANTUM 9
+
+struct fscrypt_policy {
+ __u8 version;
+ __u8 contents_encryption_mode;
+ __u8 filenames_encryption_mode;
+ __u8 flags;
+ __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+};
+
+#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
+#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
+#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
+
+/* Parameters for passing an encryption key into the kernel keyring */
+#define FS_KEY_DESC_PREFIX "fscrypt:"
+#define FS_KEY_DESC_PREFIX_SIZE 8
+
+/* Structure that userspace passes to the kernel keyring */
+#define FS_MAX_KEY_SIZE 64
+
+struct fscrypt_key {
+ __u32 mode;
+ __u8 raw[FS_MAX_KEY_SIZE];
+ __u32 size;
+};
+
+#endif /* _UAPI_LINUX_FSCRYPT_H */
--
2.21.0
^ permalink raw reply related
* [PATCH v4 02/17] fscrypt: use FSCRYPT_ prefix for uapi constants
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Prefix all filesystem encryption UAPI constants except the ioctl numbers
with "FSCRYPT_" rather than with "FS_". This namespaces the constants
more appropriately and makes it clear that they are related specifically
to the filesystem encryption feature, and to the 'fscrypt_*' structures.
With some of the old names like "FS_POLICY_FLAGS_VALID", it was not
immediately clear that the constant had anything to do with encryption.
This is also useful because we'll be adding more encryption-related
constants, e.g. for the policy version, and we'd otherwise have to
choose whether to use unclear names like FS_POLICY_V1 or inconsistent
names like FS_ENCRYPTION_POLICY_V1.
For source compatibility with existing userspace programs, keep the old
names defined as aliases to the new names.
Finally, as long as new names are being defined anyway, I skipped
defining new names for the fscrypt mode numbers that aren't actually
used: INVALID (0), AES_256_GCM (2), AES_256_CBC (3), SPECK128_256_XTS
(7), and SPECK128_256_CTS (8).
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
Documentation/filesystems/fscrypt.rst | 36 +++++++--------
include/uapi/linux/fscrypt.h | 66 +++++++++++++++++----------
2 files changed, 61 insertions(+), 41 deletions(-)
diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 08c23b60e0164..4f9df6ba669e5 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -223,9 +223,10 @@ a little endian number, except that:
is encrypted with AES-256 where the AES-256 key is the SHA-256 hash
of the file's data encryption key.
-- In the "direct key" configuration (FS_POLICY_FLAG_DIRECT_KEY set in
- the fscrypt_policy), the file's nonce is also appended to the IV.
- Currently this is only allowed with the Adiantum encryption mode.
+- In the "direct key" configuration (FSCRYPT_POLICY_FLAG_DIRECT_KEY
+ set in the fscrypt_policy), the file's nonce is also appended to the
+ IV. Currently this is only allowed with the Adiantum encryption
+ mode.
Filenames encryption
--------------------
@@ -272,14 +273,14 @@ empty directory or verifies that a directory or regular file already
has the specified encryption policy. It takes in a pointer to a
:c:type:`struct fscrypt_policy`, defined as follows::
- #define FS_KEY_DESCRIPTOR_SIZE 8
+ #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
struct fscrypt_policy {
__u8 version;
__u8 contents_encryption_mode;
__u8 filenames_encryption_mode;
__u8 flags;
- __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
};
This structure must be initialized as follows:
@@ -288,18 +289,17 @@ This structure must be initialized as follows:
- ``contents_encryption_mode`` and ``filenames_encryption_mode`` must
be set to constants from ``<linux/fs.h>`` which identify the
- encryption modes to use. If unsure, use
- FS_ENCRYPTION_MODE_AES_256_XTS (1) for ``contents_encryption_mode``
- and FS_ENCRYPTION_MODE_AES_256_CTS (4) for
- ``filenames_encryption_mode``.
+ encryption modes to use. If unsure, use FSCRYPT_MODE_AES_256_XTS
+ (1) for ``contents_encryption_mode`` and FSCRYPT_MODE_AES_256_CTS
+ (4) for ``filenames_encryption_mode``.
- ``flags`` must contain a value from ``<linux/fs.h>`` which
identifies the amount of NUL-padding to use when encrypting
- filenames. If unsure, use FS_POLICY_FLAGS_PAD_32 (0x3).
- In addition, if the chosen encryption modes are both
- FS_ENCRYPTION_MODE_ADIANTUM, this can contain
- FS_POLICY_FLAG_DIRECT_KEY to specify that the master key should be
- used directly, without key derivation.
+ filenames. If unsure, use FSCRYPT_POLICY_FLAGS_PAD_32 (0x3). In
+ addition, if the chosen encryption modes are both
+ FSCRYPT_MODE_ADIANTUM, this can contain
+ FSCRYPT_POLICY_FLAG_DIRECT_KEY to specify that the master key should
+ be used directly, without key derivation.
- ``master_key_descriptor`` specifies how to find the master key in
the keyring; see `Adding keys`_. It is up to userspace to choose a
@@ -399,11 +399,11 @@ followed by the 16-character lower case hex representation of the
``master_key_descriptor`` that was set in the encryption policy. The
key payload must conform to the following structure::
- #define FS_MAX_KEY_SIZE 64
+ #define FSCRYPT_MAX_KEY_SIZE 64
struct fscrypt_key {
u32 mode;
- u8 raw[FS_MAX_KEY_SIZE];
+ u8 raw[FSCRYPT_MAX_KEY_SIZE];
u32 size;
};
@@ -572,7 +572,7 @@ much confusion if an encryption policy were to be added to or removed
from anything other than an empty directory.) The struct is defined
as follows::
- #define FS_KEY_DESCRIPTOR_SIZE 8
+ #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
#define FS_KEY_DERIVATION_NONCE_SIZE 16
struct fscrypt_context {
@@ -580,7 +580,7 @@ as follows::
u8 contents_encryption_mode;
u8 filenames_encryption_mode;
u8 flags;
- u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
};
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index 193339cb53fdb..f9b99cc028bc6 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -8,34 +8,30 @@
* File system encryption support
*/
/* Policy provided via an ioctl on the topmost directory */
-#define FS_KEY_DESCRIPTOR_SIZE 8
+#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8
-#define FS_POLICY_FLAGS_PAD_4 0x00
-#define FS_POLICY_FLAGS_PAD_8 0x01
-#define FS_POLICY_FLAGS_PAD_16 0x02
-#define FS_POLICY_FLAGS_PAD_32 0x03
-#define FS_POLICY_FLAGS_PAD_MASK 0x03
-#define FS_POLICY_FLAG_DIRECT_KEY 0x04 /* use master key directly */
-#define FS_POLICY_FLAGS_VALID 0x07
+/* Encryption policy flags */
+#define FSCRYPT_POLICY_FLAGS_PAD_4 0x00
+#define FSCRYPT_POLICY_FLAGS_PAD_8 0x01
+#define FSCRYPT_POLICY_FLAGS_PAD_16 0x02
+#define FSCRYPT_POLICY_FLAGS_PAD_32 0x03
+#define FSCRYPT_POLICY_FLAGS_PAD_MASK 0x03
+#define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 /* use master key directly */
+#define FSCRYPT_POLICY_FLAGS_VALID 0x07
/* Encryption algorithms */
-#define FS_ENCRYPTION_MODE_INVALID 0
-#define FS_ENCRYPTION_MODE_AES_256_XTS 1
-#define FS_ENCRYPTION_MODE_AES_256_GCM 2
-#define FS_ENCRYPTION_MODE_AES_256_CBC 3
-#define FS_ENCRYPTION_MODE_AES_256_CTS 4
-#define FS_ENCRYPTION_MODE_AES_128_CBC 5
-#define FS_ENCRYPTION_MODE_AES_128_CTS 6
-#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* Removed, do not use. */
-#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* Removed, do not use. */
-#define FS_ENCRYPTION_MODE_ADIANTUM 9
+#define FSCRYPT_MODE_AES_256_XTS 1
+#define FSCRYPT_MODE_AES_256_CTS 4
+#define FSCRYPT_MODE_AES_128_CBC 5
+#define FSCRYPT_MODE_AES_128_CTS 6
+#define FSCRYPT_MODE_ADIANTUM 9
struct fscrypt_policy {
__u8 version;
__u8 contents_encryption_mode;
__u8 filenames_encryption_mode;
__u8 flags;
- __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
};
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
@@ -43,16 +39,40 @@ struct fscrypt_policy {
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
/* Parameters for passing an encryption key into the kernel keyring */
-#define FS_KEY_DESC_PREFIX "fscrypt:"
-#define FS_KEY_DESC_PREFIX_SIZE 8
+#define FSCRYPT_KEY_DESC_PREFIX "fscrypt:"
+#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8
/* Structure that userspace passes to the kernel keyring */
-#define FS_MAX_KEY_SIZE 64
+#define FSCRYPT_MAX_KEY_SIZE 64
struct fscrypt_key {
__u32 mode;
- __u8 raw[FS_MAX_KEY_SIZE];
+ __u8 raw[FSCRYPT_MAX_KEY_SIZE];
__u32 size;
};
+/**********************************************************************/
+
+/* old names; don't add anything new here! */
+#define FS_KEY_DESCRIPTOR_SIZE FSCRYPT_KEY_DESCRIPTOR_SIZE
+#define FS_POLICY_FLAGS_PAD_4 FSCRYPT_POLICY_FLAGS_PAD_4
+#define FS_POLICY_FLAGS_PAD_8 FSCRYPT_POLICY_FLAGS_PAD_8
+#define FS_POLICY_FLAGS_PAD_16 FSCRYPT_POLICY_FLAGS_PAD_16
+#define FS_POLICY_FLAGS_PAD_32 FSCRYPT_POLICY_FLAGS_PAD_32
+#define FS_POLICY_FLAGS_PAD_MASK FSCRYPT_POLICY_FLAGS_PAD_MASK
+#define FS_POLICY_FLAG_DIRECT_KEY FSCRYPT_POLICY_FLAG_DIRECT_KEY
+#define FS_POLICY_FLAGS_VALID FSCRYPT_POLICY_FLAGS_VALID
+#define FS_ENCRYPTION_MODE_INVALID 0 /* never used */
+#define FS_ENCRYPTION_MODE_AES_256_XTS FSCRYPT_MODE_AES_256_XTS
+#define FS_ENCRYPTION_MODE_AES_256_GCM 2 /* never used */
+#define FS_ENCRYPTION_MODE_AES_256_CBC 3 /* never used */
+#define FS_ENCRYPTION_MODE_AES_256_CTS FSCRYPT_MODE_AES_256_CTS
+#define FS_ENCRYPTION_MODE_AES_128_CBC FSCRYPT_MODE_AES_128_CBC
+#define FS_ENCRYPTION_MODE_AES_128_CTS FSCRYPT_MODE_AES_128_CTS
+#define FS_ENCRYPTION_MODE_SPECK128_256_XTS 7 /* removed */
+#define FS_ENCRYPTION_MODE_SPECK128_256_CTS 8 /* removed */
+#define FS_ENCRYPTION_MODE_ADIANTUM FSCRYPT_MODE_ADIANTUM
+#define FS_KEY_DESC_PREFIX FSCRYPT_KEY_DESC_PREFIX
+#define FS_KEY_DESC_PREFIX_SIZE FSCRYPT_KEY_DESC_PREFIX_SIZE
+#define FS_MAX_KEY_SIZE FSCRYPT_MAX_KEY_SIZE
#endif /* _UAPI_LINUX_FSCRYPT_H */
--
2.21.0
^ permalink raw reply related
* [PATCH v4 03/17] fscrypt: use FSCRYPT_* definitions, not FS_*
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Update fs/crypto/ to use the new names for the UAPI constants rather
than the old names, then make the old definitions conditional on
!__KERNEL__.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/crypto.c | 2 +-
fs/crypto/fname.c | 2 +-
fs/crypto/fscrypt_private.h | 16 +++++------
fs/crypto/keyinfo.c | 53 ++++++++++++++++++------------------
fs/crypto/policy.c | 14 +++++-----
include/uapi/linux/fscrypt.h | 2 ++
6 files changed, 46 insertions(+), 43 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 4dc788e3bc96b..8217a44346215 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -139,7 +139,7 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
memset(iv, 0, ci->ci_mode->ivsize);
iv->lblk_num = cpu_to_le64(lblk_num);
- if (ci->ci_flags & FS_POLICY_FLAG_DIRECT_KEY)
+ if (ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
memcpy(iv->nonce, ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE);
if (ci->ci_essiv_tfm != NULL)
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 7ff40a73dbece..37c26ad36d0e3 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -187,7 +187,7 @@ bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
u32 max_len, u32 *encrypted_len_ret)
{
int padding = 4 << (inode->i_crypt_info->ci_flags &
- FS_POLICY_FLAGS_PAD_MASK);
+ FSCRYPT_POLICY_FLAGS_PAD_MASK);
u32 encrypted_len;
if (orig_len > max_len)
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 7da2761595933..52e09ef40bfa6 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -34,7 +34,7 @@ struct fscrypt_context {
u8 contents_encryption_mode;
u8 filenames_encryption_mode;
u8 flags;
- u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
} __packed;
@@ -84,7 +84,7 @@ struct fscrypt_info {
u8 ci_data_mode;
u8 ci_filename_mode;
u8 ci_flags;
- u8 ci_master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ u8 ci_master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
u8 ci_nonce[FS_KEY_DERIVATION_NONCE_SIZE];
};
@@ -99,16 +99,16 @@ typedef enum {
static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
u32 filenames_mode)
{
- if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
- filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
+ if (contents_mode == FSCRYPT_MODE_AES_128_CBC &&
+ filenames_mode == FSCRYPT_MODE_AES_128_CTS)
return true;
- if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
- filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
+ if (contents_mode == FSCRYPT_MODE_AES_256_XTS &&
+ filenames_mode == FSCRYPT_MODE_AES_256_CTS)
return true;
- if (contents_mode == FS_ENCRYPTION_MODE_ADIANTUM &&
- filenames_mode == FS_ENCRYPTION_MODE_ADIANTUM)
+ if (contents_mode == FSCRYPT_MODE_ADIANTUM &&
+ filenames_mode == FSCRYPT_MODE_ADIANTUM)
return true;
return false;
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index 322ce9686bdba..058e2e0fda430 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -21,7 +21,7 @@
static struct crypto_shash *essiv_hash_tfm;
-/* Table of keys referenced by FS_POLICY_FLAG_DIRECT_KEY policies */
+/* Table of keys referenced by DIRECT_KEY policies */
static DEFINE_HASHTABLE(fscrypt_master_keys, 6); /* 6 bits = 64 buckets */
static DEFINE_SPINLOCK(fscrypt_master_keys_lock);
@@ -78,7 +78,7 @@ static int derive_key_aes(const u8 *master_key,
*/
static struct key *
find_and_lock_process_key(const char *prefix,
- const u8 descriptor[FS_KEY_DESCRIPTOR_SIZE],
+ const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],
unsigned int min_keysize,
const struct fscrypt_key **payload_ret)
{
@@ -88,7 +88,7 @@ find_and_lock_process_key(const char *prefix,
const struct fscrypt_key *payload;
description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
- FS_KEY_DESCRIPTOR_SIZE, descriptor);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);
if (!description)
return ERR_PTR(-ENOMEM);
@@ -106,7 +106,7 @@ find_and_lock_process_key(const char *prefix,
payload = (const struct fscrypt_key *)ukp->data;
if (ukp->datalen != sizeof(struct fscrypt_key) ||
- payload->size < 1 || payload->size > FS_MAX_KEY_SIZE) {
+ payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {
fscrypt_warn(NULL,
"key with description '%s' has invalid payload",
key->description);
@@ -130,32 +130,32 @@ find_and_lock_process_key(const char *prefix,
}
static struct fscrypt_mode available_modes[] = {
- [FS_ENCRYPTION_MODE_AES_256_XTS] = {
+ [FSCRYPT_MODE_AES_256_XTS] = {
.friendly_name = "AES-256-XTS",
.cipher_str = "xts(aes)",
.keysize = 64,
.ivsize = 16,
},
- [FS_ENCRYPTION_MODE_AES_256_CTS] = {
+ [FSCRYPT_MODE_AES_256_CTS] = {
.friendly_name = "AES-256-CTS-CBC",
.cipher_str = "cts(cbc(aes))",
.keysize = 32,
.ivsize = 16,
},
- [FS_ENCRYPTION_MODE_AES_128_CBC] = {
+ [FSCRYPT_MODE_AES_128_CBC] = {
.friendly_name = "AES-128-CBC",
.cipher_str = "cbc(aes)",
.keysize = 16,
.ivsize = 16,
.needs_essiv = true,
},
- [FS_ENCRYPTION_MODE_AES_128_CTS] = {
+ [FSCRYPT_MODE_AES_128_CTS] = {
.friendly_name = "AES-128-CTS-CBC",
.cipher_str = "cts(cbc(aes))",
.keysize = 16,
.ivsize = 16,
},
- [FS_ENCRYPTION_MODE_ADIANTUM] = {
+ [FSCRYPT_MODE_ADIANTUM] = {
.friendly_name = "Adiantum",
.cipher_str = "adiantum(xchacha12,aes)",
.keysize = 32,
@@ -194,7 +194,7 @@ static int find_and_derive_key(const struct inode *inode,
const struct fscrypt_key *payload;
int err;
- key = find_and_lock_process_key(FS_KEY_DESC_PREFIX,
+ key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
ctx->master_key_descriptor,
mode->keysize, &payload);
if (key == ERR_PTR(-ENOKEY) && inode->i_sb->s_cop->key_prefix) {
@@ -205,7 +205,7 @@ static int find_and_derive_key(const struct inode *inode,
if (IS_ERR(key))
return PTR_ERR(key);
- if (ctx->flags & FS_POLICY_FLAG_DIRECT_KEY) {
+ if (ctx->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
if (mode->ivsize < offsetofend(union fscrypt_iv, nonce)) {
fscrypt_warn(inode->i_sb,
"direct key mode not allowed with %s",
@@ -269,14 +269,14 @@ allocate_skcipher_for_mode(struct fscrypt_mode *mode, const u8 *raw_key,
return ERR_PTR(err);
}
-/* Master key referenced by FS_POLICY_FLAG_DIRECT_KEY policy */
+/* Master key referenced by DIRECT_KEY policy */
struct fscrypt_master_key {
struct hlist_node mk_node;
refcount_t mk_refcount;
const struct fscrypt_mode *mk_mode;
struct crypto_skcipher *mk_ctfm;
- u8 mk_descriptor[FS_KEY_DESCRIPTOR_SIZE];
- u8 mk_raw[FS_MAX_KEY_SIZE];
+ u8 mk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+ u8 mk_raw[FSCRYPT_MAX_KEY_SIZE];
};
static void free_master_key(struct fscrypt_master_key *mk)
@@ -317,13 +317,13 @@ find_or_insert_master_key(struct fscrypt_master_key *to_insert,
* raw key, and use crypto_memneq() when comparing raw keys.
*/
- BUILD_BUG_ON(sizeof(hash_key) > FS_KEY_DESCRIPTOR_SIZE);
+ BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE);
memcpy(&hash_key, ci->ci_master_key_descriptor, sizeof(hash_key));
spin_lock(&fscrypt_master_keys_lock);
hash_for_each_possible(fscrypt_master_keys, mk, mk_node, hash_key) {
if (memcmp(ci->ci_master_key_descriptor, mk->mk_descriptor,
- FS_KEY_DESCRIPTOR_SIZE) != 0)
+ FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
continue;
if (mode != mk->mk_mode)
continue;
@@ -367,7 +367,7 @@ fscrypt_get_master_key(const struct fscrypt_info *ci, struct fscrypt_mode *mode,
goto err_free_mk;
}
memcpy(mk->mk_descriptor, ci->ci_master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
memcpy(mk->mk_raw, raw_key, mode->keysize);
return find_or_insert_master_key(mk, raw_key, mode, ci);
@@ -446,8 +446,8 @@ void __exit fscrypt_essiv_cleanup(void)
/*
* Given the encryption mode and key (normally the derived key, but for
- * FS_POLICY_FLAG_DIRECT_KEY mode it's the master key), set up the inode's
- * symmetric cipher transform object(s).
+ * DIRECT_KEY mode it's the master key), set up the inode's symmetric cipher
+ * transform object(s).
*/
static int setup_crypto_transform(struct fscrypt_info *ci,
struct fscrypt_mode *mode,
@@ -457,7 +457,7 @@ static int setup_crypto_transform(struct fscrypt_info *ci,
struct crypto_skcipher *ctfm;
int err;
- if (ci->ci_flags & FS_POLICY_FLAG_DIRECT_KEY) {
+ if (ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
mk = fscrypt_get_master_key(ci, mode, raw_key, inode);
if (IS_ERR(mk))
return PTR_ERR(mk);
@@ -474,7 +474,7 @@ static int setup_crypto_transform(struct fscrypt_info *ci,
if (mode->needs_essiv) {
/* ESSIV implies 16-byte IVs which implies !DIRECT_KEY */
WARN_ON(mode->ivsize != AES_BLOCK_SIZE);
- WARN_ON(ci->ci_flags & FS_POLICY_FLAG_DIRECT_KEY);
+ WARN_ON(ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY);
err = init_essiv_generator(ci, raw_key, mode->keysize);
if (err) {
@@ -524,9 +524,10 @@ int fscrypt_get_encryption_info(struct inode *inode)
/* Fake up a context for an unencrypted directory */
memset(&ctx, 0, sizeof(ctx));
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
- ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
- ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
- memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
+ ctx.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
+ ctx.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
+ memset(ctx.master_key_descriptor, 0x42,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
} else if (res != sizeof(ctx)) {
return -EINVAL;
}
@@ -534,7 +535,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
return -EINVAL;
- if (ctx.flags & ~FS_POLICY_FLAGS_VALID)
+ if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID)
return -EINVAL;
crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
@@ -545,7 +546,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
crypt_info->ci_data_mode = ctx.contents_encryption_mode;
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
memcpy(crypt_info->ci_master_key_descriptor, ctx.master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
memcpy(crypt_info->ci_nonce, ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
mode = select_encryption_mode(crypt_info, inode);
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index bd7eaf9b3f003..1e0563cea1a56 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -22,7 +22,7 @@ static bool is_encryption_context_consistent_with_policy(
const struct fscrypt_policy *policy)
{
return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 &&
(ctx->flags == policy->flags) &&
(ctx->contents_encryption_mode ==
policy->contents_encryption_mode) &&
@@ -37,13 +37,13 @@ static int create_encryption_context_from_policy(struct inode *inode,
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
policy->filenames_encryption_mode))
return -EINVAL;
- if (policy->flags & ~FS_POLICY_FLAGS_VALID)
+ if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID)
return -EINVAL;
ctx.contents_encryption_mode = policy->contents_encryption_mode;
@@ -126,7 +126,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
policy.filenames_encryption_mode = ctx.filenames_encryption_mode;
policy.flags = ctx.flags;
memcpy(policy.master_key_descriptor, ctx.master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
if (copy_to_user(arg, &policy, sizeof(policy)))
return -EFAULT;
@@ -200,7 +200,7 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
if (parent_ci && child_ci) {
return memcmp(parent_ci->ci_master_key_descriptor,
child_ci->ci_master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 &&
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode ==
child_ci->ci_filename_mode) &&
@@ -217,7 +217,7 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
return memcmp(parent_ctx.master_key_descriptor,
child_ctx.master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ FSCRYPT_KEY_DESCRIPTOR_SIZE) == 0 &&
(parent_ctx.contents_encryption_mode ==
child_ctx.contents_encryption_mode) &&
(parent_ctx.filenames_encryption_mode ==
@@ -255,7 +255,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
ctx.filenames_encryption_mode = ci->ci_filename_mode;
ctx.flags = ci->ci_flags;
memcpy(ctx.master_key_descriptor, ci->ci_master_key_descriptor,
- FS_KEY_DESCRIPTOR_SIZE);
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
res = parent->i_sb->s_cop->set_context(child, &ctx,
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index f9b99cc028bc6..3bbc5dfbde211 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -53,6 +53,7 @@ struct fscrypt_key {
/**********************************************************************/
/* old names; don't add anything new here! */
+#ifndef __KERNEL__
#define FS_KEY_DESCRIPTOR_SIZE FSCRYPT_KEY_DESCRIPTOR_SIZE
#define FS_POLICY_FLAGS_PAD_4 FSCRYPT_POLICY_FLAGS_PAD_4
#define FS_POLICY_FLAGS_PAD_8 FSCRYPT_POLICY_FLAGS_PAD_8
@@ -74,5 +75,6 @@ struct fscrypt_key {
#define FS_KEY_DESC_PREFIX FSCRYPT_KEY_DESC_PREFIX
#define FS_KEY_DESC_PREFIX_SIZE FSCRYPT_KEY_DESC_PREFIX_SIZE
#define FS_MAX_KEY_SIZE FSCRYPT_MAX_KEY_SIZE
+#endif /* !__KERNEL__ */
#endif /* _UAPI_LINUX_FSCRYPT_H */
--
2.21.0
^ permalink raw reply related
* [PATCH v4 04/17] fscrypt: add ->ci_inode to fscrypt_info
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add an inode back-pointer to 'struct fscrypt_info', such that
inode->i_crypt_info->ci_inode == inode.
This will be useful for:
1. Evicting the inodes when a fscrypt key is removed, since we'll track
the inodes using a given key by linking their fscrypt_infos together,
rather than the inodes directly. This avoids bloating 'struct inode'
with a new list_head.
2. Simplifying the per-file key setup, since the inode pointer won't
have to be passed around everywhere just in case something goes wrong
and it's needed for fscrypt_warn().
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/fscrypt_private.h | 3 +++
fs/crypto/keyinfo.c | 2 ++
2 files changed, 5 insertions(+)
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 52e09ef40bfa6..ac24edfc297f1 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -73,6 +73,9 @@ struct fscrypt_info {
*/
struct fscrypt_mode *ci_mode;
+ /* Back-pointer to the inode */
+ struct inode *ci_inode;
+
/*
* If non-NULL, then this inode uses a master key directly rather than a
* derived key, and ci_ctfm will equal ci_master_key->mk_ctfm.
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index 058e2e0fda430..148a54fb556a6 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -542,6 +542,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
if (!crypt_info)
return -ENOMEM;
+ crypt_info->ci_inode = inode;
+
crypt_info->ci_flags = ctx.flags;
crypt_info->ci_data_mode = ctx.contents_encryption_mode;
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
--
2.21.0
^ permalink raw reply related
* [PATCH v4 05/17] fscrypt: refactor v1 policy key setup into keysetup_legacy.c
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
In preparation for introducing v2 encryption policies which will find
and derive encryption keys differently from the current v1 encryption
policies, refactor the v1 policy-specific key setup code from keyinfo.c
into keysetup_legacy.c. Then rename keyinfo.c to keysetup.c.
Note: the code moved into keysetup_legacy.c includes the table of master
keys referenced by v1 DIRECT_KEY policies. I've chosen to keep this
table as-is rather than trying to replace it with using the
filesystem-level keyring, since the latter would add more complexity
than it would save especially given the requirement to continue to
support the keys actually being provided in a process-subscribed
keyring. However, to distinguish the structures in this table from the
structures that will go in the filesystem-level keyring, I renamed them
from 'struct fscrypt_master_key' to 'struct fscrypt_direct_key'.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/Makefile | 8 +-
fs/crypto/fscrypt_private.h | 37 ++-
fs/crypto/keyinfo.c | 595 ------------------------------------
fs/crypto/keysetup.c | 309 +++++++++++++++++++
fs/crypto/keysetup_legacy.c | 338 ++++++++++++++++++++
include/linux/fscrypt.h | 4 +-
6 files changed, 685 insertions(+), 606 deletions(-)
delete mode 100644 fs/crypto/keyinfo.c
create mode 100644 fs/crypto/keysetup.c
create mode 100644 fs/crypto/keysetup_legacy.c
diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
index cb496989a6b69..75c0c29fcc627 100644
--- a/fs/crypto/Makefile
+++ b/fs/crypto/Makefile
@@ -1,4 +1,10 @@
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
-fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o
+fscrypto-y := crypto.o \
+ fname.o \
+ hooks.o \
+ keysetup.o \
+ keysetup_legacy.o \
+ policy.o
+
fscrypto-$(CONFIG_BLOCK) += bio.o
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index ac24edfc297f1..c5a8181fc26c1 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -4,9 +4,8 @@
*
* Copyright (C) 2015, Google, Inc.
*
- * This contains encryption key functions.
- *
- * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
+ * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar.
+ * Heavily modified since then.
*/
#ifndef _FSCRYPT_PRIVATE_H
@@ -77,11 +76,10 @@ struct fscrypt_info {
struct inode *ci_inode;
/*
- * If non-NULL, then this inode uses a master key directly rather than a
- * derived key, and ci_ctfm will equal ci_master_key->mk_ctfm.
- * Otherwise, this inode uses a derived key.
+ * If non-NULL, then encryption is done using the master key directly
+ * and ci_ctfm will equal ci_direct_key->dk_ctfm.
*/
- struct fscrypt_master_key *ci_master_key;
+ struct fscrypt_direct_key *ci_direct_key;
/* fields from the fscrypt_context */
u8 ci_data_mode;
@@ -161,7 +159,7 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
u32 orig_len, u32 max_len,
u32 *encrypted_len_ret);
-/* keyinfo.c */
+/* keysetup.c */
struct fscrypt_mode {
const char *friendly_name;
@@ -172,6 +170,29 @@ struct fscrypt_mode {
bool needs_essiv;
};
+static inline bool
+fscrypt_mode_supports_direct_key(const struct fscrypt_mode *mode)
+{
+ return mode->ivsize >= offsetofend(union fscrypt_iv, nonce);
+}
+
+extern struct crypto_skcipher *
+fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
+ const struct inode *inode);
+
+extern int fscrypt_set_derived_key(struct fscrypt_info *ci,
+ const u8 *derived_key);
+
extern void __exit fscrypt_essiv_cleanup(void);
+/* keysetup_legacy.c */
+
+extern void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+
+extern int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
+ const u8 *raw_master_key);
+
+extern int fscrypt_setup_v1_file_key_via_subscribed_keyrings(
+ struct fscrypt_info *ci);
+
#endif /* _FSCRYPT_PRIVATE_H */
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
deleted file mode 100644
index 148a54fb556a6..0000000000000
--- a/fs/crypto/keyinfo.c
+++ /dev/null
@@ -1,595 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * key management facility for FS encryption support.
- *
- * Copyright (C) 2015, Google, Inc.
- *
- * This contains encryption key functions.
- *
- * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
- */
-
-#include <keys/user-type.h>
-#include <linux/hashtable.h>
-#include <linux/scatterlist.h>
-#include <linux/ratelimit.h>
-#include <crypto/aes.h>
-#include <crypto/algapi.h>
-#include <crypto/sha.h>
-#include <crypto/skcipher.h>
-#include "fscrypt_private.h"
-
-static struct crypto_shash *essiv_hash_tfm;
-
-/* Table of keys referenced by DIRECT_KEY policies */
-static DEFINE_HASHTABLE(fscrypt_master_keys, 6); /* 6 bits = 64 buckets */
-static DEFINE_SPINLOCK(fscrypt_master_keys_lock);
-
-/*
- * Key derivation function. This generates the derived key by encrypting the
- * master key with AES-128-ECB using the inode's nonce as the AES key.
- *
- * The master key must be at least as long as the derived key. If the master
- * key is longer, then only the first 'derived_keysize' bytes are used.
- */
-static int derive_key_aes(const u8 *master_key,
- const struct fscrypt_context *ctx,
- u8 *derived_key, unsigned int derived_keysize)
-{
- int res = 0;
- struct skcipher_request *req = NULL;
- DECLARE_CRYPTO_WAIT(wait);
- struct scatterlist src_sg, dst_sg;
- struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
-
- if (IS_ERR(tfm)) {
- res = PTR_ERR(tfm);
- tfm = NULL;
- goto out;
- }
- crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
- req = skcipher_request_alloc(tfm, GFP_NOFS);
- if (!req) {
- res = -ENOMEM;
- goto out;
- }
- skcipher_request_set_callback(req,
- CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
- crypto_req_done, &wait);
- res = crypto_skcipher_setkey(tfm, ctx->nonce, sizeof(ctx->nonce));
- if (res < 0)
- goto out;
-
- sg_init_one(&src_sg, master_key, derived_keysize);
- sg_init_one(&dst_sg, derived_key, derived_keysize);
- skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,
- NULL);
- res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
-out:
- skcipher_request_free(req);
- crypto_free_skcipher(tfm);
- return res;
-}
-
-/*
- * Search the current task's subscribed keyrings for a "logon" key with
- * description prefix:descriptor, and if found acquire a read lock on it and
- * return a pointer to its validated payload in *payload_ret.
- */
-static struct key *
-find_and_lock_process_key(const char *prefix,
- const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],
- unsigned int min_keysize,
- const struct fscrypt_key **payload_ret)
-{
- char *description;
- struct key *key;
- const struct user_key_payload *ukp;
- const struct fscrypt_key *payload;
-
- description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
- FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);
- if (!description)
- return ERR_PTR(-ENOMEM);
-
- key = request_key(&key_type_logon, description, NULL);
- kfree(description);
- if (IS_ERR(key))
- return key;
-
- down_read(&key->sem);
- ukp = user_key_payload_locked(key);
-
- if (!ukp) /* was the key revoked before we acquired its semaphore? */
- goto invalid;
-
- payload = (const struct fscrypt_key *)ukp->data;
-
- if (ukp->datalen != sizeof(struct fscrypt_key) ||
- payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {
- fscrypt_warn(NULL,
- "key with description '%s' has invalid payload",
- key->description);
- goto invalid;
- }
-
- if (payload->size < min_keysize) {
- fscrypt_warn(NULL,
- "key with description '%s' is too short (got %u bytes, need %u+ bytes)",
- key->description, payload->size, min_keysize);
- goto invalid;
- }
-
- *payload_ret = payload;
- return key;
-
-invalid:
- up_read(&key->sem);
- key_put(key);
- return ERR_PTR(-ENOKEY);
-}
-
-static struct fscrypt_mode available_modes[] = {
- [FSCRYPT_MODE_AES_256_XTS] = {
- .friendly_name = "AES-256-XTS",
- .cipher_str = "xts(aes)",
- .keysize = 64,
- .ivsize = 16,
- },
- [FSCRYPT_MODE_AES_256_CTS] = {
- .friendly_name = "AES-256-CTS-CBC",
- .cipher_str = "cts(cbc(aes))",
- .keysize = 32,
- .ivsize = 16,
- },
- [FSCRYPT_MODE_AES_128_CBC] = {
- .friendly_name = "AES-128-CBC",
- .cipher_str = "cbc(aes)",
- .keysize = 16,
- .ivsize = 16,
- .needs_essiv = true,
- },
- [FSCRYPT_MODE_AES_128_CTS] = {
- .friendly_name = "AES-128-CTS-CBC",
- .cipher_str = "cts(cbc(aes))",
- .keysize = 16,
- .ivsize = 16,
- },
- [FSCRYPT_MODE_ADIANTUM] = {
- .friendly_name = "Adiantum",
- .cipher_str = "adiantum(xchacha12,aes)",
- .keysize = 32,
- .ivsize = 32,
- },
-};
-
-static struct fscrypt_mode *
-select_encryption_mode(const struct fscrypt_info *ci, const struct inode *inode)
-{
- if (!fscrypt_valid_enc_modes(ci->ci_data_mode, ci->ci_filename_mode)) {
- fscrypt_warn(inode->i_sb,
- "inode %lu uses unsupported encryption modes (contents mode %d, filenames mode %d)",
- inode->i_ino, ci->ci_data_mode,
- ci->ci_filename_mode);
- return ERR_PTR(-EINVAL);
- }
-
- if (S_ISREG(inode->i_mode))
- return &available_modes[ci->ci_data_mode];
-
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- return &available_modes[ci->ci_filename_mode];
-
- WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n",
- inode->i_ino, (inode->i_mode & S_IFMT));
- return ERR_PTR(-EINVAL);
-}
-
-/* Find the master key, then derive the inode's actual encryption key */
-static int find_and_derive_key(const struct inode *inode,
- const struct fscrypt_context *ctx,
- u8 *derived_key, const struct fscrypt_mode *mode)
-{
- struct key *key;
- const struct fscrypt_key *payload;
- int err;
-
- key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
- ctx->master_key_descriptor,
- mode->keysize, &payload);
- if (key == ERR_PTR(-ENOKEY) && inode->i_sb->s_cop->key_prefix) {
- key = find_and_lock_process_key(inode->i_sb->s_cop->key_prefix,
- ctx->master_key_descriptor,
- mode->keysize, &payload);
- }
- if (IS_ERR(key))
- return PTR_ERR(key);
-
- if (ctx->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
- if (mode->ivsize < offsetofend(union fscrypt_iv, nonce)) {
- fscrypt_warn(inode->i_sb,
- "direct key mode not allowed with %s",
- mode->friendly_name);
- err = -EINVAL;
- } else if (ctx->contents_encryption_mode !=
- ctx->filenames_encryption_mode) {
- fscrypt_warn(inode->i_sb,
- "direct key mode not allowed with different contents and filenames modes");
- err = -EINVAL;
- } else {
- memcpy(derived_key, payload->raw, mode->keysize);
- err = 0;
- }
- } else {
- err = derive_key_aes(payload->raw, ctx, derived_key,
- mode->keysize);
- }
- up_read(&key->sem);
- key_put(key);
- return err;
-}
-
-/* Allocate and key a symmetric cipher object for the given encryption mode */
-static struct crypto_skcipher *
-allocate_skcipher_for_mode(struct fscrypt_mode *mode, const u8 *raw_key,
- const struct inode *inode)
-{
- struct crypto_skcipher *tfm;
- int err;
-
- tfm = crypto_alloc_skcipher(mode->cipher_str, 0, 0);
- if (IS_ERR(tfm)) {
- fscrypt_warn(inode->i_sb,
- "error allocating '%s' transform for inode %lu: %ld",
- mode->cipher_str, inode->i_ino, PTR_ERR(tfm));
- return tfm;
- }
- if (unlikely(!mode->logged_impl_name)) {
- /*
- * fscrypt performance can vary greatly depending on which
- * crypto algorithm implementation is used. Help people debug
- * performance problems by logging the ->cra_driver_name the
- * first time a mode is used. Note that multiple threads can
- * race here, but it doesn't really matter.
- */
- mode->logged_impl_name = true;
- pr_info("fscrypt: %s using implementation \"%s\"\n",
- mode->friendly_name,
- crypto_skcipher_alg(tfm)->base.cra_driver_name);
- }
- crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
- err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize);
- if (err)
- goto err_free_tfm;
-
- return tfm;
-
-err_free_tfm:
- crypto_free_skcipher(tfm);
- return ERR_PTR(err);
-}
-
-/* Master key referenced by DIRECT_KEY policy */
-struct fscrypt_master_key {
- struct hlist_node mk_node;
- refcount_t mk_refcount;
- const struct fscrypt_mode *mk_mode;
- struct crypto_skcipher *mk_ctfm;
- u8 mk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
- u8 mk_raw[FSCRYPT_MAX_KEY_SIZE];
-};
-
-static void free_master_key(struct fscrypt_master_key *mk)
-{
- if (mk) {
- crypto_free_skcipher(mk->mk_ctfm);
- kzfree(mk);
- }
-}
-
-static void put_master_key(struct fscrypt_master_key *mk)
-{
- if (!refcount_dec_and_lock(&mk->mk_refcount, &fscrypt_master_keys_lock))
- return;
- hash_del(&mk->mk_node);
- spin_unlock(&fscrypt_master_keys_lock);
-
- free_master_key(mk);
-}
-
-/*
- * Find/insert the given master key into the fscrypt_master_keys table. If
- * found, it is returned with elevated refcount, and 'to_insert' is freed if
- * non-NULL. If not found, 'to_insert' is inserted and returned if it's
- * non-NULL; otherwise NULL is returned.
- */
-static struct fscrypt_master_key *
-find_or_insert_master_key(struct fscrypt_master_key *to_insert,
- const u8 *raw_key, const struct fscrypt_mode *mode,
- const struct fscrypt_info *ci)
-{
- unsigned long hash_key;
- struct fscrypt_master_key *mk;
-
- /*
- * Careful: to avoid potentially leaking secret key bytes via timing
- * information, we must key the hash table by descriptor rather than by
- * raw key, and use crypto_memneq() when comparing raw keys.
- */
-
- BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE);
- memcpy(&hash_key, ci->ci_master_key_descriptor, sizeof(hash_key));
-
- spin_lock(&fscrypt_master_keys_lock);
- hash_for_each_possible(fscrypt_master_keys, mk, mk_node, hash_key) {
- if (memcmp(ci->ci_master_key_descriptor, mk->mk_descriptor,
- FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
- continue;
- if (mode != mk->mk_mode)
- continue;
- if (crypto_memneq(raw_key, mk->mk_raw, mode->keysize))
- continue;
- /* using existing tfm with same (descriptor, mode, raw_key) */
- refcount_inc(&mk->mk_refcount);
- spin_unlock(&fscrypt_master_keys_lock);
- free_master_key(to_insert);
- return mk;
- }
- if (to_insert)
- hash_add(fscrypt_master_keys, &to_insert->mk_node, hash_key);
- spin_unlock(&fscrypt_master_keys_lock);
- return to_insert;
-}
-
-/* Prepare to encrypt directly using the master key in the given mode */
-static struct fscrypt_master_key *
-fscrypt_get_master_key(const struct fscrypt_info *ci, struct fscrypt_mode *mode,
- const u8 *raw_key, const struct inode *inode)
-{
- struct fscrypt_master_key *mk;
- int err;
-
- /* Is there already a tfm for this key? */
- mk = find_or_insert_master_key(NULL, raw_key, mode, ci);
- if (mk)
- return mk;
-
- /* Nope, allocate one. */
- mk = kzalloc(sizeof(*mk), GFP_NOFS);
- if (!mk)
- return ERR_PTR(-ENOMEM);
- refcount_set(&mk->mk_refcount, 1);
- mk->mk_mode = mode;
- mk->mk_ctfm = allocate_skcipher_for_mode(mode, raw_key, inode);
- if (IS_ERR(mk->mk_ctfm)) {
- err = PTR_ERR(mk->mk_ctfm);
- mk->mk_ctfm = NULL;
- goto err_free_mk;
- }
- memcpy(mk->mk_descriptor, ci->ci_master_key_descriptor,
- FSCRYPT_KEY_DESCRIPTOR_SIZE);
- memcpy(mk->mk_raw, raw_key, mode->keysize);
-
- return find_or_insert_master_key(mk, raw_key, mode, ci);
-
-err_free_mk:
- free_master_key(mk);
- return ERR_PTR(err);
-}
-
-static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt)
-{
- struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm);
-
- /* init hash transform on demand */
- if (unlikely(!tfm)) {
- struct crypto_shash *prev_tfm;
-
- tfm = crypto_alloc_shash("sha256", 0, 0);
- if (IS_ERR(tfm)) {
- fscrypt_warn(NULL,
- "error allocating SHA-256 transform: %ld",
- PTR_ERR(tfm));
- return PTR_ERR(tfm);
- }
- prev_tfm = cmpxchg(&essiv_hash_tfm, NULL, tfm);
- if (prev_tfm) {
- crypto_free_shash(tfm);
- tfm = prev_tfm;
- }
- }
-
- {
- SHASH_DESC_ON_STACK(desc, tfm);
- desc->tfm = tfm;
- desc->flags = 0;
-
- return crypto_shash_digest(desc, key, keysize, salt);
- }
-}
-
-static int init_essiv_generator(struct fscrypt_info *ci, const u8 *raw_key,
- int keysize)
-{
- int err;
- struct crypto_cipher *essiv_tfm;
- u8 salt[SHA256_DIGEST_SIZE];
-
- essiv_tfm = crypto_alloc_cipher("aes", 0, 0);
- if (IS_ERR(essiv_tfm))
- return PTR_ERR(essiv_tfm);
-
- ci->ci_essiv_tfm = essiv_tfm;
-
- err = derive_essiv_salt(raw_key, keysize, salt);
- if (err)
- goto out;
-
- /*
- * Using SHA256 to derive the salt/key will result in AES-256 being
- * used for IV generation. File contents encryption will still use the
- * configured keysize (AES-128) nevertheless.
- */
- err = crypto_cipher_setkey(essiv_tfm, salt, sizeof(salt));
- if (err)
- goto out;
-
-out:
- memzero_explicit(salt, sizeof(salt));
- return err;
-}
-
-void __exit fscrypt_essiv_cleanup(void)
-{
- crypto_free_shash(essiv_hash_tfm);
-}
-
-/*
- * Given the encryption mode and key (normally the derived key, but for
- * DIRECT_KEY mode it's the master key), set up the inode's symmetric cipher
- * transform object(s).
- */
-static int setup_crypto_transform(struct fscrypt_info *ci,
- struct fscrypt_mode *mode,
- const u8 *raw_key, const struct inode *inode)
-{
- struct fscrypt_master_key *mk;
- struct crypto_skcipher *ctfm;
- int err;
-
- if (ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
- mk = fscrypt_get_master_key(ci, mode, raw_key, inode);
- if (IS_ERR(mk))
- return PTR_ERR(mk);
- ctfm = mk->mk_ctfm;
- } else {
- mk = NULL;
- ctfm = allocate_skcipher_for_mode(mode, raw_key, inode);
- if (IS_ERR(ctfm))
- return PTR_ERR(ctfm);
- }
- ci->ci_master_key = mk;
- ci->ci_ctfm = ctfm;
-
- if (mode->needs_essiv) {
- /* ESSIV implies 16-byte IVs which implies !DIRECT_KEY */
- WARN_ON(mode->ivsize != AES_BLOCK_SIZE);
- WARN_ON(ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY);
-
- err = init_essiv_generator(ci, raw_key, mode->keysize);
- if (err) {
- fscrypt_warn(inode->i_sb,
- "error initializing ESSIV generator for inode %lu: %d",
- inode->i_ino, err);
- return err;
- }
- }
- return 0;
-}
-
-static void put_crypt_info(struct fscrypt_info *ci)
-{
- if (!ci)
- return;
-
- if (ci->ci_master_key) {
- put_master_key(ci->ci_master_key);
- } else {
- crypto_free_skcipher(ci->ci_ctfm);
- crypto_free_cipher(ci->ci_essiv_tfm);
- }
- kmem_cache_free(fscrypt_info_cachep, ci);
-}
-
-int fscrypt_get_encryption_info(struct inode *inode)
-{
- struct fscrypt_info *crypt_info;
- struct fscrypt_context ctx;
- struct fscrypt_mode *mode;
- u8 *raw_key = NULL;
- int res;
-
- if (inode->i_crypt_info)
- return 0;
-
- res = fscrypt_initialize(inode->i_sb->s_cop->flags);
- if (res)
- return res;
-
- res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
- if (res < 0) {
- if (!fscrypt_dummy_context_enabled(inode) ||
- IS_ENCRYPTED(inode))
- return res;
- /* Fake up a context for an unencrypted directory */
- memset(&ctx, 0, sizeof(ctx));
- ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
- ctx.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
- ctx.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
- memset(ctx.master_key_descriptor, 0x42,
- FSCRYPT_KEY_DESCRIPTOR_SIZE);
- } else if (res != sizeof(ctx)) {
- return -EINVAL;
- }
-
- if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
- return -EINVAL;
-
- if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID)
- return -EINVAL;
-
- crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
- if (!crypt_info)
- return -ENOMEM;
-
- crypt_info->ci_inode = inode;
-
- crypt_info->ci_flags = ctx.flags;
- crypt_info->ci_data_mode = ctx.contents_encryption_mode;
- crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
- memcpy(crypt_info->ci_master_key_descriptor, ctx.master_key_descriptor,
- FSCRYPT_KEY_DESCRIPTOR_SIZE);
- memcpy(crypt_info->ci_nonce, ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
-
- mode = select_encryption_mode(crypt_info, inode);
- if (IS_ERR(mode)) {
- res = PTR_ERR(mode);
- goto out;
- }
- WARN_ON(mode->ivsize > FSCRYPT_MAX_IV_SIZE);
- crypt_info->ci_mode = mode;
-
- /*
- * This cannot be a stack buffer because it may be passed to the
- * scatterlist crypto API as part of key derivation.
- */
- res = -ENOMEM;
- raw_key = kmalloc(mode->keysize, GFP_NOFS);
- if (!raw_key)
- goto out;
-
- res = find_and_derive_key(inode, &ctx, raw_key, mode);
- if (res)
- goto out;
-
- res = setup_crypto_transform(crypt_info, mode, raw_key, inode);
- if (res)
- goto out;
-
- if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
- crypt_info = NULL;
-out:
- if (res == -ENOKEY)
- res = 0;
- put_crypt_info(crypt_info);
- kzfree(raw_key);
- return res;
-}
-EXPORT_SYMBOL(fscrypt_get_encryption_info);
-
-void fscrypt_put_encryption_info(struct inode *inode)
-{
- put_crypt_info(inode->i_crypt_info);
- inode->i_crypt_info = NULL;
-}
-EXPORT_SYMBOL(fscrypt_put_encryption_info);
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
new file mode 100644
index 0000000000000..829b74974e8f6
--- /dev/null
+++ b/fs/crypto/keysetup.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Key setup facility for FS encryption support.
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * Originally written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar.
+ * Heavily modified since then.
+ */
+
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/skcipher.h>
+#include <linux/key.h>
+
+#include "fscrypt_private.h"
+
+static struct crypto_shash *essiv_hash_tfm;
+
+static struct fscrypt_mode available_modes[] = {
+ [FSCRYPT_MODE_AES_256_XTS] = {
+ .friendly_name = "AES-256-XTS",
+ .cipher_str = "xts(aes)",
+ .keysize = 64,
+ .ivsize = 16,
+ },
+ [FSCRYPT_MODE_AES_256_CTS] = {
+ .friendly_name = "AES-256-CTS-CBC",
+ .cipher_str = "cts(cbc(aes))",
+ .keysize = 32,
+ .ivsize = 16,
+ },
+ [FSCRYPT_MODE_AES_128_CBC] = {
+ .friendly_name = "AES-128-CBC",
+ .cipher_str = "cbc(aes)",
+ .keysize = 16,
+ .ivsize = 16,
+ .needs_essiv = true,
+ },
+ [FSCRYPT_MODE_AES_128_CTS] = {
+ .friendly_name = "AES-128-CTS-CBC",
+ .cipher_str = "cts(cbc(aes))",
+ .keysize = 16,
+ .ivsize = 16,
+ },
+ [FSCRYPT_MODE_ADIANTUM] = {
+ .friendly_name = "Adiantum",
+ .cipher_str = "adiantum(xchacha12,aes)",
+ .keysize = 32,
+ .ivsize = 32,
+ },
+};
+
+static struct fscrypt_mode *
+select_encryption_mode(const struct fscrypt_info *ci, const struct inode *inode)
+{
+ if (!fscrypt_valid_enc_modes(ci->ci_data_mode, ci->ci_filename_mode)) {
+ fscrypt_warn(inode->i_sb,
+ "inode %lu uses unsupported encryption modes (contents mode %d, filenames mode %d)",
+ inode->i_ino, ci->ci_data_mode,
+ ci->ci_filename_mode);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (S_ISREG(inode->i_mode))
+ return &available_modes[ci->ci_data_mode];
+
+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+ return &available_modes[ci->ci_filename_mode];
+
+ WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n",
+ inode->i_ino, (inode->i_mode & S_IFMT));
+ return ERR_PTR(-EINVAL);
+}
+
+/* Create a symmetric cipher object for the given encryption mode and key */
+struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
+ const u8 *raw_key,
+ const struct inode *inode)
+{
+ struct crypto_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_skcipher(mode->cipher_str, 0, 0);
+ if (IS_ERR(tfm)) {
+ fscrypt_warn(inode->i_sb,
+ "error allocating '%s' transform for inode %lu: %ld",
+ mode->cipher_str, inode->i_ino, PTR_ERR(tfm));
+ return tfm;
+ }
+ if (unlikely(!mode->logged_impl_name)) {
+ /*
+ * fscrypt performance can vary greatly depending on which
+ * crypto algorithm implementation is used. Help people debug
+ * performance problems by logging the ->cra_driver_name the
+ * first time a mode is used. Note that multiple threads can
+ * race here, but it doesn't really matter.
+ */
+ mode->logged_impl_name = true;
+ pr_info("fscrypt: %s using implementation \"%s\"\n",
+ mode->friendly_name,
+ crypto_skcipher_alg(tfm)->base.cra_driver_name);
+ }
+ crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
+ err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize);
+ if (err)
+ goto err_free_tfm;
+
+ return tfm;
+
+err_free_tfm:
+ crypto_free_skcipher(tfm);
+ return ERR_PTR(err);
+}
+
+static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt)
+{
+ struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm);
+
+ /* init hash transform on demand */
+ if (unlikely(!tfm)) {
+ struct crypto_shash *prev_tfm;
+
+ tfm = crypto_alloc_shash("sha256", 0, 0);
+ if (IS_ERR(tfm)) {
+ fscrypt_warn(NULL,
+ "error allocating SHA-256 transform: %ld",
+ PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+ prev_tfm = cmpxchg(&essiv_hash_tfm, NULL, tfm);
+ if (prev_tfm) {
+ crypto_free_shash(tfm);
+ tfm = prev_tfm;
+ }
+ }
+
+ {
+ SHASH_DESC_ON_STACK(desc, tfm);
+ desc->tfm = tfm;
+ desc->flags = 0;
+
+ return crypto_shash_digest(desc, key, keysize, salt);
+ }
+}
+
+static int init_essiv_generator(struct fscrypt_info *ci, const u8 *raw_key,
+ int keysize)
+{
+ int err;
+ struct crypto_cipher *essiv_tfm;
+ u8 salt[SHA256_DIGEST_SIZE];
+
+ if (WARN_ON(ci->ci_mode->ivsize != AES_BLOCK_SIZE))
+ return -EINVAL;
+
+ essiv_tfm = crypto_alloc_cipher("aes", 0, 0);
+ if (IS_ERR(essiv_tfm))
+ return PTR_ERR(essiv_tfm);
+
+ ci->ci_essiv_tfm = essiv_tfm;
+
+ err = derive_essiv_salt(raw_key, keysize, salt);
+ if (err)
+ goto out;
+
+ /*
+ * Using SHA256 to derive the salt/key will result in AES-256 being
+ * used for IV generation. File contents encryption will still use the
+ * configured keysize (AES-128) nevertheless.
+ */
+ err = crypto_cipher_setkey(essiv_tfm, salt, sizeof(salt));
+ if (err)
+ goto out;
+
+out:
+ memzero_explicit(salt, sizeof(salt));
+ return err;
+}
+
+void __exit fscrypt_essiv_cleanup(void)
+{
+ crypto_free_shash(essiv_hash_tfm);
+}
+
+/* Given the per-file key, set up the file's crypto transform object(s) */
+int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key)
+{
+ struct fscrypt_mode *mode = ci->ci_mode;
+ struct crypto_skcipher *ctfm;
+ int err;
+
+ ctfm = fscrypt_allocate_skcipher(mode, derived_key, ci->ci_inode);
+ if (IS_ERR(ctfm))
+ return PTR_ERR(ctfm);
+
+ ci->ci_ctfm = ctfm;
+
+ if (mode->needs_essiv) {
+ err = init_essiv_generator(ci, derived_key, mode->keysize);
+ if (err) {
+ fscrypt_warn(ci->ci_inode->i_sb,
+ "error initializing ESSIV generator for inode %lu: %d",
+ ci->ci_inode->i_ino, err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find the master key, then set up the inode's actual encryption key.
+ */
+static int setup_file_encryption_key(struct fscrypt_info *ci)
+{
+ return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+}
+
+static void put_crypt_info(struct fscrypt_info *ci)
+{
+ if (!ci)
+ return;
+
+ if (ci->ci_direct_key) {
+ fscrypt_put_direct_key(ci->ci_direct_key);
+ } else {
+ crypto_free_skcipher(ci->ci_ctfm);
+ crypto_free_cipher(ci->ci_essiv_tfm);
+ }
+ kmem_cache_free(fscrypt_info_cachep, ci);
+}
+
+int fscrypt_get_encryption_info(struct inode *inode)
+{
+ struct fscrypt_info *crypt_info;
+ struct fscrypt_context ctx;
+ struct fscrypt_mode *mode;
+ int res;
+
+ if (inode->i_crypt_info)
+ return 0;
+
+ res = fscrypt_initialize(inode->i_sb->s_cop->flags);
+ if (res)
+ return res;
+
+ res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
+ if (res < 0) {
+ if (!fscrypt_dummy_context_enabled(inode) ||
+ IS_ENCRYPTED(inode))
+ return res;
+ /* Fake up a context for an unencrypted directory */
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
+ ctx.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
+ ctx.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
+ memset(ctx.master_key_descriptor, 0x42,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ } else if (res != sizeof(ctx)) {
+ return -EINVAL;
+ }
+
+ if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
+ return -EINVAL;
+
+ if (ctx.flags & ~FSCRYPT_POLICY_FLAGS_VALID)
+ return -EINVAL;
+
+ crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
+ if (!crypt_info)
+ return -ENOMEM;
+
+ crypt_info->ci_inode = inode;
+
+ crypt_info->ci_flags = ctx.flags;
+ crypt_info->ci_data_mode = ctx.contents_encryption_mode;
+ crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
+ memcpy(crypt_info->ci_master_key_descriptor, ctx.master_key_descriptor,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ memcpy(crypt_info->ci_nonce, ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
+
+ mode = select_encryption_mode(crypt_info, inode);
+ if (IS_ERR(mode)) {
+ res = PTR_ERR(mode);
+ goto out;
+ }
+ WARN_ON(mode->ivsize > FSCRYPT_MAX_IV_SIZE);
+ crypt_info->ci_mode = mode;
+
+ res = setup_file_encryption_key(crypt_info);
+ if (res)
+ goto out;
+
+ if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
+ crypt_info = NULL;
+out:
+ if (res == -ENOKEY)
+ res = 0;
+ put_crypt_info(crypt_info);
+ return res;
+}
+EXPORT_SYMBOL(fscrypt_get_encryption_info);
+
+void fscrypt_put_encryption_info(struct inode *inode)
+{
+ put_crypt_info(inode->i_crypt_info);
+ inode->i_crypt_info = NULL;
+}
+EXPORT_SYMBOL(fscrypt_put_encryption_info);
diff --git a/fs/crypto/keysetup_legacy.c b/fs/crypto/keysetup_legacy.c
new file mode 100644
index 0000000000000..407daa0b64d82
--- /dev/null
+++ b/fs/crypto/keysetup_legacy.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Key setup for v1 encryption policies
+ *
+ * Copyright 2015, 2019 Google LLC
+ */
+
+/*
+ * This file implements compatibility functions for the original encryption
+ * policy version ("v1"), including:
+ *
+ * - Deriving per-file keys using the AES-128-ECB based KDF
+ * (rather than the new method of using HKDF-SHA512)
+ *
+ * - Retrieving fscrypt master keys from process-subscribed keyrings
+ * (rather than the new method of using a filesystem-level keyring)
+ *
+ * - Handling policies with the DIRECT_KEY flag set using a master key table
+ * (rather than the new method of implementing DIRECT_KEY with per-mode keys
+ * managed alongside the master keys in the filesystem-level keyring)
+ */
+
+#include <crypto/algapi.h>
+#include <crypto/skcipher.h>
+#include <keys/user-type.h>
+#include <linux/hashtable.h>
+#include <linux/scatterlist.h>
+
+#include "fscrypt_private.h"
+
+/* Table of keys referenced by DIRECT_KEY policies */
+static DEFINE_HASHTABLE(fscrypt_direct_keys, 6); /* 6 bits = 64 buckets */
+static DEFINE_SPINLOCK(fscrypt_direct_keys_lock);
+
+/*
+ * Legacy key derivation function. This generates the derived key by encrypting
+ * the master key with AES-128-ECB using the nonce as the AES key. This
+ * provides a unique derived key with sufficient entropy for each inode.
+ * However, it's nonstandard, non-extensible, doesn't evenly distribute the
+ * entropy from the master key, and is trivially reversible: an attacker who
+ * compromises a derived key can "decrypt" it to get back to the master key,
+ * then derive any other key. For all new code, use HKDF instead.
+ *
+ * The master key must be at least as long as the derived key. If the master
+ * key is longer, then only the first 'derived_keysize' bytes are used.
+ */
+static int derive_key_aes(const u8 *master_key,
+ const u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE],
+ u8 *derived_key, unsigned int derived_keysize)
+{
+ int res = 0;
+ struct skcipher_request *req = NULL;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct scatterlist src_sg, dst_sg;
+ struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
+
+ if (IS_ERR(tfm)) {
+ res = PTR_ERR(tfm);
+ tfm = NULL;
+ goto out;
+ }
+ crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_FORBID_WEAK_KEYS);
+ req = skcipher_request_alloc(tfm, GFP_NOFS);
+ if (!req) {
+ res = -ENOMEM;
+ goto out;
+ }
+ skcipher_request_set_callback(req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &wait);
+ res = crypto_skcipher_setkey(tfm, nonce, FS_KEY_DERIVATION_NONCE_SIZE);
+ if (res < 0)
+ goto out;
+
+ sg_init_one(&src_sg, master_key, derived_keysize);
+ sg_init_one(&dst_sg, derived_key, derived_keysize);
+ skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,
+ NULL);
+ res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
+out:
+ skcipher_request_free(req);
+ crypto_free_skcipher(tfm);
+ return res;
+}
+
+/*
+ * Search the current task's subscribed keyrings for a "logon" key with
+ * description prefix:descriptor, and if found acquire a read lock on it and
+ * return a pointer to its validated payload in *payload_ret.
+ */
+static struct key *
+find_and_lock_process_key(const char *prefix,
+ const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],
+ unsigned int min_keysize,
+ const struct fscrypt_key **payload_ret)
+{
+ char *description;
+ struct key *key;
+ const struct user_key_payload *ukp;
+ const struct fscrypt_key *payload;
+
+ description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);
+ if (!description)
+ return ERR_PTR(-ENOMEM);
+
+ key = request_key(&key_type_logon, description, NULL);
+ kfree(description);
+ if (IS_ERR(key))
+ return key;
+
+ down_read(&key->sem);
+ ukp = user_key_payload_locked(key);
+
+ if (!ukp) /* was the key revoked before we acquired its semaphore? */
+ goto invalid;
+
+ payload = (const struct fscrypt_key *)ukp->data;
+
+ if (ukp->datalen != sizeof(struct fscrypt_key) ||
+ payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {
+ fscrypt_warn(NULL,
+ "key with description '%s' has invalid payload",
+ key->description);
+ goto invalid;
+ }
+
+ if (payload->size < min_keysize) {
+ fscrypt_warn(NULL,
+ "key with description '%s' is too short (got %u bytes, need %u+ bytes)",
+ key->description, payload->size, min_keysize);
+ goto invalid;
+ }
+
+ *payload_ret = payload;
+ return key;
+
+invalid:
+ up_read(&key->sem);
+ key_put(key);
+ return ERR_PTR(-ENOKEY);
+}
+
+/* Master key referenced by DIRECT_KEY policy */
+struct fscrypt_direct_key {
+ struct hlist_node dk_node;
+ refcount_t dk_refcount;
+ const struct fscrypt_mode *dk_mode;
+ struct crypto_skcipher *dk_ctfm;
+ u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+ u8 dk_raw[FSCRYPT_MAX_KEY_SIZE];
+};
+
+static void free_direct_key(struct fscrypt_direct_key *dk)
+{
+ if (dk) {
+ crypto_free_skcipher(dk->dk_ctfm);
+ kzfree(dk);
+ }
+}
+
+void fscrypt_put_direct_key(struct fscrypt_direct_key *dk)
+{
+ if (!refcount_dec_and_lock(&dk->dk_refcount, &fscrypt_direct_keys_lock))
+ return;
+ hash_del(&dk->dk_node);
+ spin_unlock(&fscrypt_direct_keys_lock);
+
+ free_direct_key(dk);
+}
+
+/*
+ * Find/insert the given key into the fscrypt_direct_keys table. If found, it
+ * is returned with elevated refcount, and 'to_insert' is freed if non-NULL. If
+ * not found, 'to_insert' is inserted and returned if it's non-NULL; otherwise
+ * NULL is returned.
+ */
+static struct fscrypt_direct_key *
+find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
+ const u8 *raw_key, const struct fscrypt_info *ci)
+{
+ unsigned long hash_key;
+ struct fscrypt_direct_key *dk;
+
+ /*
+ * Careful: to avoid potentially leaking secret key bytes via timing
+ * information, we must key the hash table by descriptor rather than by
+ * raw key, and use crypto_memneq() when comparing raw keys.
+ */
+
+ BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ memcpy(&hash_key, ci->ci_master_key_descriptor, sizeof(hash_key));
+
+ spin_lock(&fscrypt_direct_keys_lock);
+ hash_for_each_possible(fscrypt_direct_keys, dk, dk_node, hash_key) {
+ if (memcmp(ci->ci_master_key_descriptor, dk->dk_descriptor,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
+ continue;
+ if (ci->ci_mode != dk->dk_mode)
+ continue;
+ if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))
+ continue;
+ /* using existing tfm with same (descriptor, mode, raw_key) */
+ refcount_inc(&dk->dk_refcount);
+ spin_unlock(&fscrypt_direct_keys_lock);
+ free_direct_key(to_insert);
+ return dk;
+ }
+ if (to_insert)
+ hash_add(fscrypt_direct_keys, &to_insert->dk_node, hash_key);
+ spin_unlock(&fscrypt_direct_keys_lock);
+ return to_insert;
+}
+
+/* Prepare to encrypt directly using the master key in the given mode */
+static struct fscrypt_direct_key *
+fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
+{
+ struct fscrypt_direct_key *dk;
+ int err;
+
+ /* Is there already a tfm for this key? */
+ dk = find_or_insert_direct_key(NULL, raw_key, ci);
+ if (dk)
+ return dk;
+
+ /* Nope, allocate one. */
+ dk = kzalloc(sizeof(*dk), GFP_NOFS);
+ if (!dk)
+ return ERR_PTR(-ENOMEM);
+ refcount_set(&dk->dk_refcount, 1);
+ dk->dk_mode = ci->ci_mode;
+ dk->dk_ctfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key,
+ ci->ci_inode);
+ if (IS_ERR(dk->dk_ctfm)) {
+ err = PTR_ERR(dk->dk_ctfm);
+ dk->dk_ctfm = NULL;
+ goto err_free_dk;
+ }
+ memcpy(dk->dk_descriptor, ci->ci_master_key_descriptor,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize);
+
+ return find_or_insert_direct_key(dk, raw_key, ci);
+
+err_free_dk:
+ free_direct_key(dk);
+ return ERR_PTR(err);
+}
+
+/* v1 policy, DIRECT_KEY: use the master key directly */
+static int setup_v1_file_key_direct(struct fscrypt_info *ci,
+ const u8 *raw_master_key)
+{
+ const struct fscrypt_mode *mode = ci->ci_mode;
+ struct fscrypt_direct_key *dk;
+
+ if (!fscrypt_mode_supports_direct_key(mode)) {
+ fscrypt_warn(ci->ci_inode->i_sb,
+ "direct key flag not allowed with %s",
+ mode->friendly_name);
+ return -EINVAL;
+ }
+
+ if (ci->ci_data_mode != ci->ci_filename_mode) {
+ fscrypt_warn(ci->ci_inode->i_sb,
+ "direct key flag not allowed with different contents and filenames modes");
+ return -EINVAL;
+ }
+
+ /* ESSIV implies 16-byte IVs which implies !DIRECT_KEY */
+ if (WARN_ON(mode->needs_essiv))
+ return -EINVAL;
+
+ dk = fscrypt_get_direct_key(ci, raw_master_key);
+ if (IS_ERR(dk))
+ return PTR_ERR(dk);
+ ci->ci_direct_key = dk;
+ ci->ci_ctfm = dk->dk_ctfm;
+ return 0;
+}
+
+/* v1 policy, !DIRECT_KEY: derive the file's encryption key */
+static int setup_v1_file_key_derived(struct fscrypt_info *ci,
+ const u8 *raw_master_key)
+{
+ u8 *derived_key;
+ int err;
+
+ /*
+ * This cannot be a stack buffer because it will be passed to the
+ * scatterlist crypto API during derive_key_aes().
+ */
+ derived_key = kmalloc(ci->ci_mode->keysize, GFP_NOFS);
+ if (!derived_key)
+ return -ENOMEM;
+
+ err = derive_key_aes(raw_master_key, ci->ci_nonce,
+ derived_key, ci->ci_mode->keysize);
+ if (err)
+ goto out;
+
+ err = fscrypt_set_derived_key(ci, derived_key);
+out:
+ kzfree(derived_key);
+ return err;
+}
+
+int fscrypt_setup_v1_file_key(struct fscrypt_info *ci, const u8 *raw_master_key)
+{
+ if (ci->ci_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
+ return setup_v1_file_key_direct(ci, raw_master_key);
+ else
+ return setup_v1_file_key_derived(ci, raw_master_key);
+}
+
+int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci)
+{
+ struct key *key;
+ const struct fscrypt_key *payload;
+ int err;
+
+ key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
+ ci->ci_master_key_descriptor,
+ ci->ci_mode->keysize, &payload);
+ if (key == ERR_PTR(-ENOKEY) && ci->ci_inode->i_sb->s_cop->key_prefix) {
+ key = find_and_lock_process_key(ci->ci_inode->i_sb->s_cop->key_prefix,
+ ci->ci_master_key_descriptor,
+ ci->ci_mode->keysize, &payload);
+ }
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ err = fscrypt_setup_v1_file_key(ci, payload->raw);
+ up_read(&key->sem);
+ key_put(key);
+ return err;
+}
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 8b1e444214973..5afb9fc13ef49 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -112,7 +112,7 @@ extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
extern int fscrypt_inherit_context(struct inode *, struct inode *,
void *, bool);
-/* keyinfo.c */
+/* keysetup.c */
extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *);
@@ -312,7 +312,7 @@ static inline int fscrypt_inherit_context(struct inode *parent,
return -EOPNOTSUPP;
}
-/* keyinfo.c */
+/* keysetup.c */
static inline int fscrypt_get_encryption_info(struct inode *inode)
{
return -EOPNOTSUPP;
--
2.21.0
^ permalink raw reply related
* [PATCH v4 06/17] fscrypt: add FS_IOC_ADD_ENCRYPTION_KEY ioctl
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add a new fscrypt ioctl, FS_IOC_ADD_ENCRYPTION_KEY. This ioctl adds an
encryption key to the filesystem's fscrypt keyring ->s_master_keys,
making any files encrypted with that key appear "unlocked".
Why we need this
~~~~~~~~~~~~~~~~
The main problem is that the "locked/unlocked" (ciphertext/plaintext)
status of encrypted files is global, but the fscrypt keys are not.
fscrypt only looks for keys in the keyring(s) the process accessing the
filesystem is subscribed to: the thread keyring, process keyring, and
session keyring, where the session keyring may contain the user keyring.
Therefore, userspace has to put fscrypt keys in the keyrings for
individual users or sessions. But this means that when a process with a
different keyring tries to access encrypted files, whether they appear
"unlocked" or not is nondeterministic. This is because it depends on
whether the files are currently present in the inode cache.
Fixing this by consistently providing each process its own view of the
filesystem depending on whether it has the key or not isn't feasible due
to how the VFS caches work. Furthermore, while sometimes users expect
this behavior, it is misguided for two reasons. First, it would be an
OS-level access control mechanism largely redundant with existing access
control mechanisms such as UNIX file permissions, ACLs, LSMs, etc.
Encryption is actually for protecting the data at rest.
Second, almost all users of fscrypt actually do need the keys to be
global. The largest users of fscrypt, Android and Chromium OS, achieve
this by having PID 1 create a "session keyring" that is inherited by
every process. This works, but it isn't scalable because it prevents
session keyrings from being used for any other purpose.
On general-purpose Linux distros, the 'fscrypt' userspace tool [1] can't
similarly abuse the session keyring, so to make 'sudo' work on all
systems it has to link all the user keyrings into root's user keyring
[2]. This is ugly and raises security concerns. Moreover it can't make
the keys available to system services, such as sshd trying to access the
user's '~/.ssh' directory (see [3], [4]) or NetworkManager trying to
read certificates from the user's home directory (see [5]).
By having an API to add a key to the *filesystem* we'll be able to fix
the above bugs, remove userspace workarounds, and clearly express the
intended semantics: the locked/unlocked status of an encrypted directory
is global, and encryption is orthogonal to OS-level access control.
Why not use the add_key() syscall
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We use an ioctl for this API rather than the existing add_key() system
call because the ioctl gives us the flexibility needed to implement
fscrypt-specific semantics that will be introduced in later patches:
- Supporting key removal with the semantics such that the secret is
removed immediately and any unused inodes using the key are evicted;
also, the eviction of any in-use inodes can be retried.
- Calculating a key-dependent cryptographic identifier and returning it
to userspace.
- Allowing keys to be added and removed by non-root users, but only keys
for v2 encryption policies; and to prevent denial-of-service attacks,
users can only remove keys they themselves have added, and a key is
only really removed after all users who added it have removed it.
Trying to shoehorn these semantics into the keyrings syscalls would be
very difficult, whereas the ioctls make things much easier.
However, to reuse code the implementation still uses the keyrings
service internally. Thus we get lockless RCU-mode key lookups without
having to re-implement it, and the keys automatically show up in
/proc/keys for debugging purposes.
References:
[1] https://github.com/google/fscrypt
[2] https://goo.gl/55cCrI#heading=h.vf09isp98isb
[3] https://github.com/google/fscrypt/issues/111#issuecomment-444347939
[4] https://github.com/google/fscrypt/issues/116
[5] https://bugs.launchpad.net/ubuntu/+source/fscrypt/+bug/1770715
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/Makefile | 1 +
fs/crypto/crypto.c | 11 +-
fs/crypto/fscrypt_private.h | 44 +++++-
fs/crypto/keyring.c | 294 +++++++++++++++++++++++++++++++++++
fs/crypto/keysetup.c | 34 +++-
fs/super.c | 2 +
include/linux/fs.h | 1 +
include/linux/fscrypt.h | 15 ++
include/uapi/linux/fscrypt.h | 40 +++--
9 files changed, 429 insertions(+), 13 deletions(-)
create mode 100644 fs/crypto/keyring.c
diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
index 75c0c29fcc627..accdd622c9083 100644
--- a/fs/crypto/Makefile
+++ b/fs/crypto/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
fscrypto-y := crypto.o \
fname.o \
hooks.o \
+ keyring.o \
keysetup.o \
keysetup_legacy.o \
policy.o
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 8217a44346215..8726c4e940773 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -451,6 +451,8 @@ void fscrypt_msg(struct super_block *sb, const char *level,
*/
static int __init fscrypt_init(void)
{
+ int err = -ENOMEM;
+
/*
* Use an unbound workqueue to allow bios to be decrypted in parallel
* even when they happen to complete on the same CPU. This sacrifices
@@ -473,14 +475,20 @@ static int __init fscrypt_init(void)
if (!fscrypt_info_cachep)
goto fail_free_ctx;
+ err = fscrypt_init_keyring();
+ if (err)
+ goto fail_free_info;
+
return 0;
+fail_free_info:
+ kmem_cache_destroy(fscrypt_info_cachep);
fail_free_ctx:
kmem_cache_destroy(fscrypt_ctx_cachep);
fail_free_queue:
destroy_workqueue(fscrypt_read_workqueue);
fail:
- return -ENOMEM;
+ return err;
}
module_init(fscrypt_init)
@@ -497,6 +505,7 @@ static void __exit fscrypt_exit(void)
kmem_cache_destroy(fscrypt_info_cachep);
fscrypt_essiv_cleanup();
+ fscrypt_exit_keyring();
}
module_exit(fscrypt_exit);
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index c5a8181fc26c1..b4c4312085554 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -14,9 +14,12 @@
#include <linux/fscrypt.h>
#include <crypto/hash.h>
-/* Encryption parameters */
+#define CONST_STRLEN(str) (sizeof(str) - 1)
+
#define FS_KEY_DERIVATION_NONCE_SIZE 16
+#define FSCRYPT_MIN_KEY_SIZE 16
+
/**
* Encryption context for inode
*
@@ -159,6 +162,45 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
u32 orig_len, u32 max_len,
u32 *encrypted_len_ret);
+/* keyring.c */
+
+/*
+ * fscrypt_master_key_secret - secret key material of an in-use master key
+ */
+struct fscrypt_master_key_secret {
+
+ /* Size of the raw key in bytes */
+ u32 size;
+
+ /* The raw key */
+ u8 raw[FSCRYPT_MAX_KEY_SIZE];
+
+} __randomize_layout;
+
+/*
+ * fscrypt_master_key - an in-use master key
+ *
+ * This represents a master encryption key which has been added to the
+ * filesystem and can be used to "unlock" the encrypted files which were
+ * encrypted with it.
+ */
+struct fscrypt_master_key {
+
+ /* The secret key material */
+ struct fscrypt_master_key_secret mk_secret;
+
+ /* Arbitrary key descriptor which was assigned by userspace */
+ struct fscrypt_key_specifier mk_spec;
+
+} __randomize_layout;
+
+extern struct key *
+fscrypt_find_master_key(struct super_block *sb,
+ const struct fscrypt_key_specifier *mk_spec);
+
+extern int __init fscrypt_init_keyring(void);
+extern void fscrypt_exit_keyring(void);
+
/* keysetup.c */
struct fscrypt_mode {
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
new file mode 100644
index 0000000000000..011aeb58806f7
--- /dev/null
+++ b/fs/crypto/keyring.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Filesystem-level keyring for fscrypt
+ *
+ * Copyright 2019 Google LLC
+ */
+
+/*
+ * This file implements management of fscrypt master keys in the
+ * filesystem-level keyring, including the ioctls:
+ *
+ * - FS_IOC_ADD_ENCRYPTION_KEY: add a key
+ */
+
+#include <linux/key-type.h>
+#include <linux/seq_file.h>
+
+#include "fscrypt_private.h"
+
+static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret)
+{
+ memzero_explicit(secret, sizeof(*secret));
+}
+
+static void move_master_key_secret(struct fscrypt_master_key_secret *dst,
+ struct fscrypt_master_key_secret *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+ memzero_explicit(src, sizeof(*src));
+}
+
+static void free_master_key(struct fscrypt_master_key *mk)
+{
+ wipe_master_key_secret(&mk->mk_secret);
+ kzfree(mk);
+}
+
+static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec)
+{
+ switch (spec->type) {
+ case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
+ return FSCRYPT_KEY_DESCRIPTOR_SIZE;
+ }
+ return 0;
+}
+
+static inline bool valid_key_spec(const struct fscrypt_key_specifier *spec)
+{
+ if (spec->__reserved)
+ return false;
+ return master_key_spec_len(spec) != 0;
+}
+
+static int fscrypt_key_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
+{
+ key->payload.data[0] = (struct fscrypt_master_key *)prep->data;
+ return 0;
+}
+
+static void fscrypt_key_destroy(struct key *key)
+{
+ free_master_key(key->payload.data[0]);
+}
+
+static void fscrypt_key_describe(const struct key *key, struct seq_file *m)
+{
+ seq_puts(m, key->description);
+}
+
+/*
+ * Type of key in ->s_master_keys. Each key of this type represents a master
+ * key which has been added to the filesystem. Its payload is a
+ * 'struct fscrypt_master_key'. The "." prefix in the key type name prevents
+ * users from adding keys of this type via the keyrings syscalls rather than via
+ * the intended method of FS_IOC_ADD_ENCRYPTION_KEY.
+ */
+static struct key_type key_type_fscrypt = {
+ .name = "._fscrypt",
+ .instantiate = fscrypt_key_instantiate,
+ .destroy = fscrypt_key_destroy,
+ .describe = fscrypt_key_describe,
+};
+
+/* Search ->s_master_keys */
+static struct key *search_fscrypt_keyring(struct key *keyring,
+ struct key_type *type,
+ const char *description)
+{
+ /*
+ * We need to mark the keyring reference as "possessed" so that we
+ * acquire permission to search it, via the KEY_POS_SEARCH permission.
+ */
+ key_ref_t keyref = make_key_ref(keyring, true);
+
+ keyref = keyring_search(keyref, type, description);
+ if (IS_ERR(keyref)) {
+ if (PTR_ERR(keyref) == -EAGAIN || /* not found */
+ PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */
+ keyref = ERR_PTR(-ENOKEY);
+ return ERR_CAST(keyref);
+ }
+ return key_ref_to_ptr(keyref);
+}
+
+#define FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE \
+ (CONST_STRLEN("fscrypt-") + FIELD_SIZEOF(struct super_block, s_id))
+
+#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1)
+
+static void format_fs_keyring_description(
+ char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE],
+ const struct super_block *sb)
+{
+ sprintf(description, "fscrypt-%s", sb->s_id);
+}
+
+static void format_mk_description(
+ char description[FSCRYPT_MK_DESCRIPTION_SIZE],
+ const struct fscrypt_key_specifier *mk_spec)
+{
+ sprintf(description, "%*phN",
+ master_key_spec_len(mk_spec), (u8 *)&mk_spec->u);
+}
+
+/* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mutex. */
+static int allocate_filesystem_keyring(struct super_block *sb)
+{
+ char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE];
+ struct key *keyring;
+
+ if (sb->s_master_keys)
+ return 0;
+
+ format_fs_keyring_description(description, sb);
+ keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ /* Pairs with smp_load_acquire() in fscrypt_find_master_key() */
+ smp_store_release(&sb->s_master_keys, keyring);
+ return 0;
+}
+
+void fscrypt_sb_free(struct super_block *sb)
+{
+ key_put(sb->s_master_keys);
+ sb->s_master_keys = NULL;
+}
+
+/*
+ * Find the specified master key in ->s_master_keys.
+ * Returns ERR_PTR(-ENOKEY) if not found.
+ */
+struct key *fscrypt_find_master_key(struct super_block *sb,
+ const struct fscrypt_key_specifier *mk_spec)
+{
+ struct key *keyring;
+ char description[FSCRYPT_MK_DESCRIPTION_SIZE];
+
+ /* pairs with smp_store_release() in allocate_filesystem_keyring() */
+ keyring = smp_load_acquire(&sb->s_master_keys);
+ if (keyring == NULL)
+ return ERR_PTR(-ENOKEY); /* No keyring yet, so no keys yet. */
+
+ format_mk_description(description, mk_spec);
+ return search_fscrypt_keyring(keyring, &key_type_fscrypt, description);
+}
+
+/*
+ * Allocate a new fscrypt_master_key which contains the given secret, set it as
+ * the payload of a new 'struct key' of type fscrypt, and link the 'struct key'
+ * into the given keyring. Synchronized by fscrypt_add_key_mutex.
+ */
+static int add_new_master_key(struct fscrypt_master_key_secret *secret,
+ const struct fscrypt_key_specifier *mk_spec,
+ struct key *keyring)
+{
+ struct fscrypt_master_key *mk;
+ char description[FSCRYPT_MK_DESCRIPTION_SIZE];
+ struct key *key;
+ int err;
+
+ mk = kzalloc(sizeof(*mk), GFP_NOFS);
+ if (!mk)
+ return -ENOMEM;
+
+ mk->mk_spec = *mk_spec;
+
+ move_master_key_secret(&mk->mk_secret, secret);
+
+ format_mk_description(description, mk_spec);
+ key = key_alloc(&key_type_fscrypt, description,
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
+ KEY_POS_SEARCH | KEY_USR_SEARCH | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ if (IS_ERR(key)) {
+ err = PTR_ERR(key);
+ goto out_free_mk;
+ }
+ err = key_instantiate_and_link(key, mk, sizeof(*mk), keyring, NULL);
+ key_put(key);
+ if (err)
+ goto out_free_mk;
+
+ return 0;
+
+out_free_mk:
+ free_master_key(mk);
+ return err;
+}
+
+static int add_master_key(struct super_block *sb,
+ struct fscrypt_master_key_secret *secret,
+ const struct fscrypt_key_specifier *mk_spec)
+{
+ static DEFINE_MUTEX(fscrypt_add_key_mutex);
+ struct key *key;
+ int err;
+
+ mutex_lock(&fscrypt_add_key_mutex); /* serialize find + link */
+ key = fscrypt_find_master_key(sb, mk_spec);
+ if (IS_ERR(key)) {
+ err = PTR_ERR(key);
+ if (err != -ENOKEY)
+ goto out_unlock;
+ /* Didn't find the key in ->s_master_keys. Add it. */
+ err = allocate_filesystem_keyring(sb);
+ if (err)
+ goto out_unlock;
+ err = add_new_master_key(secret, mk_spec, sb->s_master_keys);
+ } else {
+ key_put(key);
+ err = 0;
+ }
+out_unlock:
+ mutex_unlock(&fscrypt_add_key_mutex);
+ return err;
+}
+
+/*
+ * Add a master encryption key to the filesystem, causing all files which were
+ * encrypted with it to appear "unlocked" (decrypted) when accessed.
+ */
+int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
+{
+ struct super_block *sb = file_inode(filp)->i_sb;
+ struct fscrypt_add_key_arg __user *uarg = _uarg;
+ struct fscrypt_add_key_arg arg;
+ struct fscrypt_master_key_secret secret;
+ int err;
+
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ if (!valid_key_spec(&arg.key_spec))
+ return -EINVAL;
+
+ if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE ||
+ arg.raw_size > FSCRYPT_MAX_KEY_SIZE)
+ return -EINVAL;
+
+ if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
+ return -EINVAL;
+
+ memset(&secret, 0, sizeof(secret));
+ secret.size = arg.raw_size;
+ err = -EFAULT;
+ if (copy_from_user(secret.raw, uarg->raw, secret.size))
+ goto out_wipe_secret;
+
+ err = -EACCES;
+ if (!capable(CAP_SYS_ADMIN))
+ goto out_wipe_secret;
+
+ err = add_master_key(sb, &secret, &arg.key_spec);
+out_wipe_secret:
+ wipe_master_key_secret(&secret);
+ return err;
+}
+EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
+
+int __init fscrypt_init_keyring(void)
+{
+ return register_key_type(&key_type_fscrypt);
+}
+
+void fscrypt_exit_keyring(void)
+{
+ unregister_key_type(&key_type_fscrypt);
+}
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 829b74974e8f6..f3a476df97c64 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -213,7 +213,39 @@ int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key)
*/
static int setup_file_encryption_key(struct fscrypt_info *ci)
{
- return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+ struct key *key;
+ struct fscrypt_master_key *mk = NULL;
+ struct fscrypt_key_specifier mk_spec;
+ int err;
+
+ mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
+ memcpy(mk_spec.u.descriptor, ci->ci_master_key_descriptor,
+ FSCRYPT_KEY_DESCRIPTOR_SIZE);
+
+ key = fscrypt_find_master_key(ci->ci_inode->i_sb, &mk_spec);
+ if (IS_ERR(key)) {
+ if (key != ERR_PTR(-ENOKEY))
+ return PTR_ERR(key);
+
+ return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+ }
+
+ mk = key->payload.data[0];
+
+ if (mk->mk_secret.size < ci->ci_mode->keysize) {
+ fscrypt_warn(NULL,
+ "key with description '%s' is too short (got %u bytes, need %u+ bytes)",
+ key->description, mk->mk_secret.size,
+ ci->ci_mode->keysize);
+ err = -ENOKEY;
+ goto out_release_key;
+ }
+
+ err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
+
+out_release_key:
+ key_put(key);
+ return err;
}
static void put_crypt_info(struct fscrypt_info *ci)
diff --git a/fs/super.c b/fs/super.c
index 583a0124bc394..ae3661ade8f32 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -32,6 +32,7 @@
#include <linux/backing-dev.h>
#include <linux/rculist_bl.h>
#include <linux/cleancache.h>
+#include <linux/fscrypt.h>
#include <linux/fsnotify.h>
#include <linux/lockdep.h>
#include <linux/user_namespace.h>
@@ -290,6 +291,7 @@ static void __put_super(struct super_block *s)
WARN_ON(s->s_inode_lru.node);
WARN_ON(!list_empty(&s->s_mounts));
security_sb_free(s);
+ fscrypt_sb_free(s);
put_user_ns(s->s_user_ns);
kfree(s->s_subtype);
call_rcu(&s->rcu, destroy_super_rcu);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 8b42df09b04c9..17c6a74860e1c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1420,6 +1420,7 @@ struct super_block {
const struct xattr_handler **s_xattr;
#ifdef CONFIG_FS_ENCRYPTION
const struct fscrypt_operations *s_cop;
+ struct key *s_master_keys; /* master crypto keys in use */
#endif
struct hlist_bl_head s_roots; /* alternate root dentries for NFS */
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 5afb9fc13ef49..e43c61fcb1125 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -112,6 +112,10 @@ extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
extern int fscrypt_inherit_context(struct inode *, struct inode *,
void *, bool);
+/* keyring.c */
+extern void fscrypt_sb_free(struct super_block *sb);
+extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
+
/* keysetup.c */
extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *);
@@ -312,6 +316,17 @@ static inline int fscrypt_inherit_context(struct inode *parent,
return -EOPNOTSUPP;
}
+/* keyring.c */
+static inline void fscrypt_sb_free(struct super_block *sb)
+{
+ return;
+}
+
+static inline int fscrypt_ioctl_add_key(struct file *filp, void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
/* keysetup.c */
static inline int fscrypt_get_encryption_info(struct inode *inode)
{
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index 3bbc5dfbde211..7bed24632bda7 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -34,22 +34,42 @@ struct fscrypt_policy {
__u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
};
-#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
-#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
-#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
-
-/* Parameters for passing an encryption key into the kernel keyring */
+/*
+ * Process-subscribed "logon" key description prefix and payload format.
+ * Deprecated; prefer FS_IOC_ADD_ENCRYPTION_KEY instead.
+ */
#define FSCRYPT_KEY_DESC_PREFIX "fscrypt:"
-#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8
-
-/* Structure that userspace passes to the kernel keyring */
-#define FSCRYPT_MAX_KEY_SIZE 64
-
+#define FSCRYPT_KEY_DESC_PREFIX_SIZE 8
+#define FSCRYPT_MAX_KEY_SIZE 64
struct fscrypt_key {
__u32 mode;
__u8 raw[FSCRYPT_MAX_KEY_SIZE];
__u32 size;
};
+
+struct fscrypt_key_specifier {
+#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1
+ __u32 type;
+ __u32 __reserved;
+ union {
+ __u8 __reserved[32]; /* reserve some extra space */
+ __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+ } u;
+};
+
+/* Struct passed to FS_IOC_ADD_ENCRYPTION_KEY */
+struct fscrypt_add_key_arg {
+ struct fscrypt_key_specifier key_spec;
+ __u32 raw_size;
+ __u32 __reserved[9];
+ __u8 raw[];
+};
+
+#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
+#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
+#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
+#define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct fscrypt_add_key_arg)
+
/**********************************************************************/
/* old names; don't add anything new here! */
--
2.21.0
^ permalink raw reply related
* [PATCH v4 07/17] fs/dcache.c: add shrink_dcache_inode()
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
When a filesystem encryption key is removed, we need all files which had
been "unlocked" (had ->i_crypt_info set up) with it to appear "locked"
again. This is most easily done by evicting the inodes. This can
currently be done using 'echo 2 > /proc/sys/vm/drop_caches'; however,
that is overkill and not usable by non-root users.
To evict just the needed inodes we also need the ability to evict those
inodes' dentries, since an inode is pinned by its dentries. Therefore,
add a function shrink_dcache_inode() which iterates through an inode's
dentries and evicts any unused ones as well as any unused descendants
(since there may be negative dentries pinning the inode's dentries).
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/dcache.c | 32 ++++++++++++++++++++++++++++++++
include/linux/dcache.h | 1 +
2 files changed, 33 insertions(+)
diff --git a/fs/dcache.c b/fs/dcache.c
index aac41adf47433..1d940484c2d17 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1507,6 +1507,38 @@ void shrink_dcache_parent(struct dentry *parent)
}
EXPORT_SYMBOL(shrink_dcache_parent);
+/**
+ * shrink_dcache_inode - prune dcache for inode
+ * @inode: inode to prune
+ *
+ * Evict all unused aliases of the specified inode from the dcache. This is
+ * intended to be used when trying to evict a specific inode, since inodes are
+ * pinned by their dentries. We also have to descend to ->d_subdirs for each
+ * alias, since aliases may be pinned by negative child dentries.
+ */
+void shrink_dcache_inode(struct inode *inode)
+{
+ for (;;) {
+ struct select_data data;
+ struct dentry *dentry;
+
+ INIT_LIST_HEAD(&data.dispose);
+ data.start = NULL;
+ data.found = 0;
+
+ spin_lock(&inode->i_lock);
+ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias)
+ d_walk(dentry, &data, select_collect);
+ spin_unlock(&inode->i_lock);
+
+ if (!data.found)
+ break;
+
+ shrink_dentry_list(&data.dispose);
+ cond_resched();
+ }
+}
+
static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
{
/* it has busy descendents; complain about those instead */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 60996e64c5798..1b5f295dc1156 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -246,6 +246,7 @@ extern struct dentry * d_obtain_alias(struct inode *);
extern struct dentry * d_obtain_root(struct inode *);
extern void shrink_dcache_sb(struct super_block *);
extern void shrink_dcache_parent(struct dentry *);
+extern void shrink_dcache_inode(struct inode *);
extern void shrink_dcache_for_umount(struct super_block *);
extern void d_invalidate(struct dentry *);
--
2.21.0
^ permalink raw reply related
* [PATCH v4 08/17] fscrypt: add FS_IOC_REMOVE_ENCRYPTION_KEY ioctl
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add a new fscrypt ioctl, FS_IOC_REMOVE_ENCRYPTION_KEY. This ioctl
removes an encryption key that was added by FS_IOC_ADD_ENCRYPTION_KEY.
It wipes the secret key itself, then "locks" the encrypted files and
directories that had been unlocked using that key -- implemented by
evicting the relevant dentries and inodes from the VFS caches.
The problem this solves is that many fscrypt users want the ability to
remove encryption keys, causing the corresponding encrypted directories
to appear "locked" (presented in ciphertext form) again. Moreover,
users want removing an encryption key to *really* remove it, in the
sense that the removed keys cannot be recovered even if kernel memory is
compromised, e.g. by the exploit of a kernel security vulnerability or
by a physical attack. This is desirable after a user logs out of the
system, for example. In many cases users even already assume this to be
the case and are surprised to hear when it's not.
It is not sufficient to simply unlink the master key from the keyring
(or to revoke or invalidate it), since the actual encryption transform
objects are still pinned in memory by their inodes. Therefore, to
really remove a key we must also evict the relevant inodes.
Currently one workaround is to run 'sync && echo 2 >
/proc/sys/vm/drop_caches'. But, that evicts all unused inodes in the
system rather than just the inodes associated with the key being
removed, causing severe performance problems. Moreover, it requires
root privileges, so regular users can't "lock" their encrypted files.
Another workaround, used in Chromium OS kernels, is to add a new
VFS-level ioctl FS_IOC_DROP_CACHE which is a more restricted version of
drop_caches that operates on a single super_block. It does:
shrink_dcache_sb(sb);
invalidate_inodes(sb, false);
But it's still a hack. Yet, the major users of filesystem encryption
want this feature badly enough that they are actually using these hacks.
To properly solve the problem, start maintaining a list of the inodes
which have been "unlocked" using each master key. Originally this
wasn't possible because the kernel didn't keep track of in-use master
keys at all. But, with the ->s_master_keys keyring it is now possible.
Then, add an ioctl FS_IOC_REMOVE_ENCRYPTION_KEY. It finds the specified
master key in ->s_master_keys, then wipes the secret key itself, which
prevents any additional inodes from being unlocked with the key. Then,
it syncs the filesystem and evicts the inodes in the key's list. The
normal inode eviction code will free and wipe the per-file keys (in
->i_crypt_info). Note that freeing ->i_crypt_info without evicting the
inodes was also considered, but would have been racy.
Some inodes may still be in use when a master key is removed, and we
can't simply revoke random file descriptors, mmap's, etc. Thus, the
ioctl simply skips in-use inodes, and returns -EBUSY to indicate that
some inodes weren't evicted. The master key *secret* is still removed,
but the fscrypt_master_key struct remains to keep track of the remaining
inodes. Userspace can then retry the ioctl to evict the remaining
inodes. Alternatively, if userspace adds the key again, the refreshed
secret will be associated with the existing list of inodes so they
remain correctly tracked for future key removals.
The ioctl doesn't wipe pagecache pages. Thus, we tolerate that after a
kernel compromise some portions of plaintext file contents may still be
recoverable from memory. This can be solved be enabling page poisoning
system-wide, which security conscious users may choose to do. But it's
very difficult to solve otherwise, e.g. note that plaintext file
contents may have been read in other places than pagecache pages.
Like FS_IOC_ADD_ENCRYPTION_KEY, FS_IOC_REMOVE_ENCRYPTION_KEY is
initially restricted to privileged users only. This is sufficient for
some use cases, but not all. A later patch will relax this restriction,
but it will require introducing key hashes, among other changes.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/fscrypt_private.h | 53 ++++++++-
fs/crypto/keyring.c | 201 ++++++++++++++++++++++++++++++++++-
fs/crypto/keysetup.c | 103 +++++++++++++++++-
include/linux/fscrypt.h | 13 +++
include/uapi/linux/fscrypt.h | 7 ++
5 files changed, 372 insertions(+), 5 deletions(-)
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index b4c4312085554..cc1862c9baa1d 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -78,6 +78,19 @@ struct fscrypt_info {
/* Back-pointer to the inode */
struct inode *ci_inode;
+ /*
+ * The master key with which this inode was unlocked (decrypted). This
+ * will be NULL if the master key was found in a process-subscribed
+ * keyring rather than in the filesystem-level keyring.
+ */
+ struct key *ci_master_key;
+
+ /*
+ * Link in list of inodes that were unlocked with the master key.
+ * Only used when ->ci_master_key is set.
+ */
+ struct list_head ci_master_key_link;
+
/*
* If non-NULL, then encryption is done using the master key directly
* and ci_ctfm will equal ci_direct_key->dk_ctfm.
@@ -186,14 +199,52 @@ struct fscrypt_master_key_secret {
*/
struct fscrypt_master_key {
- /* The secret key material */
+ /*
+ * The secret key material. After FS_IOC_REMOVE_ENCRYPTION_KEY is
+ * executed, this is wiped and no new inodes can be unlocked with this
+ * key; however, there may still be inodes in ->mk_decrypted_inodes
+ * which could not be evicted. As long as some inodes still remain,
+ * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or
+ * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
+ *
+ * Locking: protected by key->sem.
+ */
struct fscrypt_master_key_secret mk_secret;
/* Arbitrary key descriptor which was assigned by userspace */
struct fscrypt_key_specifier mk_spec;
+ /*
+ * Length of ->mk_decrypted_inodes, plus one if mk_secret is present.
+ * Once this goes to 0, the master key is removed from ->s_master_keys.
+ * The 'struct fscrypt_master_key' will continue to live as long as the
+ * 'struct key' whose payload it is, but we won't let this reference
+ * count rise again.
+ */
+ refcount_t mk_refcount;
+
+ /*
+ * List of inodes that were unlocked using this key. This allows the
+ * inodes to be evicted efficiently if the key is removed.
+ */
+ struct list_head mk_decrypted_inodes;
+ spinlock_t mk_decrypted_inodes_lock;
+
} __randomize_layout;
+static inline bool
+is_master_key_secret_present(const struct fscrypt_master_key_secret *secret)
+{
+ /*
+ * The READ_ONCE() is only necessary for fscrypt_drop_inode() and
+ * fscrypt_key_describe(). These run in atomic context, so they can't
+ * take key->sem and thus 'secret' can change concurrently which would
+ * be a data race. But they only need to know whether the secret *was*
+ * present at the time of check, so READ_ONCE() suffices.
+ */
+ return READ_ONCE(secret->size) != 0;
+}
+
extern struct key *
fscrypt_find_master_key(struct super_block *sb,
const struct fscrypt_key_specifier *mk_spec);
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 011aeb58806f7..ee9be002c1490 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -10,6 +10,7 @@
* filesystem-level keyring, including the ioctls:
*
* - FS_IOC_ADD_ENCRYPTION_KEY: add a key
+ * - FS_IOC_REMOVE_ENCRYPTION_KEY: remove a key
*/
#include <linux/key-type.h>
@@ -66,6 +67,13 @@ static void fscrypt_key_destroy(struct key *key)
static void fscrypt_key_describe(const struct key *key, struct seq_file *m)
{
seq_puts(m, key->description);
+
+ if (key_is_positive(key)) {
+ const struct fscrypt_master_key *mk = key->payload.data[0];
+
+ if (!is_master_key_secret_present(&mk->mk_secret))
+ seq_puts(m, ": secret removed");
+ }
}
/*
@@ -192,6 +200,10 @@ static int add_new_master_key(struct fscrypt_master_key_secret *secret,
move_master_key_secret(&mk->mk_secret, secret);
+ refcount_set(&mk->mk_refcount, 1); /* secret is present */
+ INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
+ spin_lock_init(&mk->mk_decrypted_inodes_lock);
+
format_mk_description(description, mk_spec);
key = key_alloc(&key_type_fscrypt, description,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
@@ -213,6 +225,22 @@ static int add_new_master_key(struct fscrypt_master_key_secret *secret,
return err;
}
+#define KEY_DEAD 1
+
+static int add_existing_master_key(struct fscrypt_master_key *mk,
+ struct fscrypt_master_key_secret *secret,
+ const struct fscrypt_key_specifier *mk_spec)
+{
+ if (is_master_key_secret_present(&mk->mk_secret))
+ return 0;
+
+ if (!refcount_inc_not_zero(&mk->mk_refcount))
+ return KEY_DEAD;
+
+ move_master_key_secret(&mk->mk_secret, secret);
+ return 0;
+}
+
static int add_master_key(struct super_block *sb,
struct fscrypt_master_key_secret *secret,
const struct fscrypt_key_specifier *mk_spec)
@@ -222,6 +250,7 @@ static int add_master_key(struct super_block *sb,
int err;
mutex_lock(&fscrypt_add_key_mutex); /* serialize find + link */
+retry:
key = fscrypt_find_master_key(sb, mk_spec);
if (IS_ERR(key)) {
err = PTR_ERR(key);
@@ -233,8 +262,21 @@ static int add_master_key(struct super_block *sb,
goto out_unlock;
err = add_new_master_key(secret, mk_spec, sb->s_master_keys);
} else {
+ /*
+ * Found the key in ->s_master_keys. Re-add the secret if
+ * needed.
+ */
+ down_write(&key->sem);
+ err = add_existing_master_key(key->payload.data[0], secret,
+ mk_spec);
+ up_write(&key->sem);
+ if (err == KEY_DEAD) {
+ /* Key being removed or needs to be removed */
+ key_invalidate(key);
+ key_put(key);
+ goto retry;
+ }
key_put(key);
- err = 0;
}
out_unlock:
mutex_unlock(&fscrypt_add_key_mutex);
@@ -283,6 +325,163 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
}
EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
+static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk)
+{
+ struct fscrypt_info *ci;
+ struct inode *inode;
+ struct inode *toput_inode = NULL;
+
+ spin_lock(&mk->mk_decrypted_inodes_lock);
+
+ list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) {
+ inode = ci->ci_inode;
+ spin_lock(&inode->i_lock);
+ if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) {
+ spin_unlock(&inode->i_lock);
+ continue;
+ }
+ __iget(inode);
+ spin_unlock(&inode->i_lock);
+ spin_unlock(&mk->mk_decrypted_inodes_lock);
+
+ shrink_dcache_inode(inode);
+ iput(toput_inode);
+ toput_inode = inode;
+
+ spin_lock(&mk->mk_decrypted_inodes_lock);
+ }
+
+ spin_unlock(&mk->mk_decrypted_inodes_lock);
+ iput(toput_inode);
+}
+
+static int try_to_lock_encrypted_files(struct super_block *sb,
+ struct fscrypt_master_key *mk)
+{
+ int err1;
+ int err2;
+
+ /*
+ * An inode can't be evicted while it is dirty or has dirty pages.
+ * Thus, we first have to clean the inodes in ->mk_decrypted_inodes.
+ *
+ * Just do it the easy way: call sync_filesystem(). It's overkill, but
+ * it works, and it's more important to minimize the amount of caches we
+ * drop than the amount of data we sync. Also, unprivileged users can
+ * already call sync_filesystem() via sys_syncfs() or sys_sync().
+ */
+ down_read(&sb->s_umount);
+ err1 = sync_filesystem(sb);
+ up_read(&sb->s_umount);
+ /* If a sync error occurs, still try to evict as much as possible. */
+
+ /*
+ * Inodes are pinned by their dentries, so we have to evict their
+ * dentries. shrink_dcache_sb() would suffice, but would be overkill
+ * and inappropriate for use by unprivileged users. So instead go
+ * through the inodes' alias lists and try to evict each dentry.
+ */
+ evict_dentries_for_decrypted_inodes(mk);
+
+ /*
+ * evict_dentries_for_decrypted_inodes() already iput() each inode in
+ * the list; any inodes for which that dropped the last reference will
+ * have been evicted due to fscrypt_drop_inode() detecting the key
+ * removal and telling the VFS to evict the inode. So to finish, we
+ * just need to check whether any inodes couldn't be evicted.
+ */
+ err2 = 0;
+ spin_lock(&mk->mk_decrypted_inodes_lock);
+ if (!list_empty(&mk->mk_decrypted_inodes)) {
+ const struct fscrypt_info *first_ci =
+ list_first_entry(&mk->mk_decrypted_inodes,
+ struct fscrypt_info,
+ ci_master_key_link);
+ fscrypt_warn(sb,
+ "inodes still busy after removing key with description %*phN (first ino: %lu)",
+ master_key_spec_len(&mk->mk_spec),
+ (u8 *)&mk->mk_spec.u, first_ci->ci_inode->i_ino);
+ err2 = -EBUSY;
+ }
+ spin_unlock(&mk->mk_decrypted_inodes_lock);
+
+ return err1 ?: err2;
+}
+
+/*
+ * Try to remove an fscrypt master encryption key. If other users have also
+ * added the key, we'll remove the current user's usage of the key, then return
+ * -EUSERS. Otherwise we'll continue on and try to actually remove the key.
+ *
+ * First we wipe the actual master key secret from memory, so that no more
+ * inodes can be unlocked with it. Then, we try to evict all cached inodes that
+ * had been unlocked using the key. Since this can fail for in-use inodes, this
+ * is expected to be used in cooperation with userspace ensuring that none of
+ * the files are still open.
+ *
+ * If, nevertheless, some inodes could not be evicted, we return -EBUSY
+ * (although we still evicted as many inodes as possible) and keep the 'struct
+ * key' and the 'struct fscrypt_master_key' around to keep track of the list of
+ * remaining inodes. Userspace can then retry the ioctl later to retry evicting
+ * the remaining inodes, or alternatively can add the secret key again.
+ *
+ * Note that even though we wipe the encryption *keys* from memory, decrypted
+ * data can likely still be found in memory, e.g. in pagecache pages that have
+ * been freed. Wiping such data is currently out of scope, short of users who
+ * may choose to enable page and slab poisoning systemwide.
+ */
+int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg)
+{
+ struct super_block *sb = file_inode(filp)->i_sb;
+ struct fscrypt_remove_key_arg arg;
+ struct key *key;
+ struct fscrypt_master_key *mk;
+ int err;
+ bool dead;
+
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ if (!valid_key_spec(&arg.key_spec))
+ return -EINVAL;
+
+ if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
+ return -EINVAL;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ /* Find the key being removed. */
+ key = fscrypt_find_master_key(sb, &arg.key_spec);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+ mk = key->payload.data[0];
+
+ down_write(&key->sem);
+
+ /* Wipe the secret. */
+ dead = false;
+ if (is_master_key_secret_present(&mk->mk_secret)) {
+ wipe_master_key_secret(&mk->mk_secret);
+ dead = refcount_dec_and_test(&mk->mk_refcount);
+ }
+ up_write(&key->sem);
+ if (dead) {
+ /*
+ * We wiped the secret and no inodes reference the key anymore,
+ * so it's free to remove.
+ */
+ key_invalidate(key);
+ err = 0;
+ } else {
+ /* Some inodes still reference this key; try to evict them. */
+ err = try_to_lock_encrypted_files(sb, mk);
+ }
+ key_put(key);
+ return err;
+}
+EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key);
+
int __init fscrypt_init_keyring(void)
{
return register_key_type(&key_type_fscrypt);
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index f3a476df97c64..f8fb115c63ab9 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -210,8 +210,16 @@ int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key)
/*
* Find the master key, then set up the inode's actual encryption key.
+ *
+ * If the master key is found in the filesystem-level keyring, then the
+ * corresponding 'struct key' is returned in *master_key_ret with
+ * ->sem read-locked. This is needed to ensure that only one task links the
+ * fscrypt_info into ->mk_decrypted_inodes (as multiple tasks may race to create
+ * an fscrypt_info for the same inode), and to synchronize the key being removed
+ * with a new inode starting to use it.
*/
-static int setup_file_encryption_key(struct fscrypt_info *ci)
+static int setup_file_encryption_key(struct fscrypt_info *ci,
+ struct key **master_key_ret)
{
struct key *key;
struct fscrypt_master_key *mk = NULL;
@@ -231,6 +239,13 @@ static int setup_file_encryption_key(struct fscrypt_info *ci)
}
mk = key->payload.data[0];
+ down_read(&key->sem);
+
+ /* Has the secret been removed (via FS_IOC_REMOVE_ENCRYPTION_KEY)? */
+ if (!is_master_key_secret_present(&mk->mk_secret)) {
+ err = -ENOKEY;
+ goto out_release_key;
+ }
if (mk->mk_secret.size < ci->ci_mode->keysize) {
fscrypt_warn(NULL,
@@ -242,14 +257,22 @@ static int setup_file_encryption_key(struct fscrypt_info *ci)
}
err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
+ if (err)
+ goto out_release_key;
+
+ *master_key_ret = key;
+ return 0;
out_release_key:
+ up_read(&key->sem);
key_put(key);
return err;
}
static void put_crypt_info(struct fscrypt_info *ci)
{
+ struct key *key;
+
if (!ci)
return;
@@ -259,6 +282,26 @@ static void put_crypt_info(struct fscrypt_info *ci)
crypto_free_skcipher(ci->ci_ctfm);
crypto_free_cipher(ci->ci_essiv_tfm);
}
+
+ key = ci->ci_master_key;
+ if (key) {
+ struct fscrypt_master_key *mk = key->payload.data[0];
+
+ /*
+ * Remove this inode from the list of inodes that were unlocked
+ * with the master key.
+ *
+ * In addition, if we're removing the last inode from a key that
+ * already had its secret removed, invalidate the key so that it
+ * gets removed from ->s_master_keys.
+ */
+ spin_lock(&mk->mk_decrypted_inodes_lock);
+ list_del(&ci->ci_master_key_link);
+ spin_unlock(&mk->mk_decrypted_inodes_lock);
+ if (refcount_dec_and_test(&mk->mk_refcount))
+ key_invalidate(key);
+ key_put(key);
+ }
kmem_cache_free(fscrypt_info_cachep, ci);
}
@@ -267,6 +310,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
struct fscrypt_info *crypt_info;
struct fscrypt_context ctx;
struct fscrypt_mode *mode;
+ struct key *master_key = NULL;
int res;
if (inode->i_crypt_info)
@@ -319,13 +363,30 @@ int fscrypt_get_encryption_info(struct inode *inode)
WARN_ON(mode->ivsize > FSCRYPT_MAX_IV_SIZE);
crypt_info->ci_mode = mode;
- res = setup_file_encryption_key(crypt_info);
+ res = setup_file_encryption_key(crypt_info, &master_key);
if (res)
goto out;
- if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
+ if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
+ if (master_key) {
+ struct fscrypt_master_key *mk =
+ master_key->payload.data[0];
+
+ refcount_inc(&mk->mk_refcount);
+ crypt_info->ci_master_key = key_get(master_key);
+ spin_lock(&mk->mk_decrypted_inodes_lock);
+ list_add(&crypt_info->ci_master_key_link,
+ &mk->mk_decrypted_inodes);
+ spin_unlock(&mk->mk_decrypted_inodes_lock);
+ }
crypt_info = NULL;
+ }
+ res = 0;
out:
+ if (master_key) {
+ up_read(&master_key->sem);
+ key_put(master_key);
+ }
if (res == -ENOKEY)
res = 0;
put_crypt_info(crypt_info);
@@ -339,3 +400,39 @@ void fscrypt_put_encryption_info(struct inode *inode)
inode->i_crypt_info = NULL;
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);
+
+/**
+ * fscrypt_drop_inode() - check whether the inode's master key has been removed
+ *
+ * Filesystems supporting fscrypt must call this from their ->drop_inode()
+ * method so that encrypted inodes are evicted as soon as they're no longer in
+ * use and their master key has been removed.
+ *
+ * Return: 1 if fscrypt wants the inode to be evicted now, otherwise 0
+ */
+int fscrypt_drop_inode(struct inode *inode)
+{
+ const struct fscrypt_info *ci = inode->i_crypt_info;
+ const struct fscrypt_master_key *mk;
+
+ /*
+ * If ci is NULL, then the inode doesn't have an encryption key set up
+ * so it's irrelevant. If ci_master_key is NULL, then the master key
+ * was provided via the legacy mechanism of the process-subscribed
+ * keyrings, so we don't know whether it's been removed or not.
+ */
+ if (!ci || !ci->ci_master_key)
+ return 0;
+ mk = ci->ci_master_key->payload.data[0];
+
+ /*
+ * Note: since we aren't holding key->sem, the result here can
+ * immediately become outdated. But there's no correctness problem with
+ * unnecessarily evicting. Nor is there a correctness problem with not
+ * evicting while iput() is racing with the key being removed, since
+ * then the thread removing the key will either evict the inode itself
+ * or will correctly detect that it wasn't evicted due to the race.
+ */
+ return !is_master_key_secret_present(&mk->mk_secret);
+}
+EXPORT_SYMBOL(fscrypt_drop_inode);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index e43c61fcb1125..b6f8832dadba4 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -115,10 +115,12 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *,
/* keyring.c */
extern void fscrypt_sb_free(struct super_block *sb);
extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
+extern int fscrypt_ioctl_remove_key(struct file *filp, const void __user *arg);
/* keysetup.c */
extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *);
+extern int fscrypt_drop_inode(struct inode *inode);
/* fname.c */
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
@@ -327,6 +329,12 @@ static inline int fscrypt_ioctl_add_key(struct file *filp, void __user *arg)
return -EOPNOTSUPP;
}
+static inline int fscrypt_ioctl_remove_key(struct file *filp,
+ const void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
/* keysetup.c */
static inline int fscrypt_get_encryption_info(struct inode *inode)
{
@@ -338,6 +346,11 @@ static inline void fscrypt_put_encryption_info(struct inode *inode)
return;
}
+static inline int fscrypt_drop_inode(struct inode *inode)
+{
+ return 0;
+}
+
/* fname.c */
static inline int fscrypt_setup_filename(struct inode *dir,
const struct qstr *iname,
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index 7bed24632bda7..3302a407131e6 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -65,10 +65,17 @@ struct fscrypt_add_key_arg {
__u8 raw[];
};
+/* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */
+struct fscrypt_remove_key_arg {
+ struct fscrypt_key_specifier key_spec;
+ __u32 __reserved[6];
+};
+
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
#define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct fscrypt_add_key_arg)
+#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOW('f', 24, struct fscrypt_remove_key_arg)
/**********************************************************************/
--
2.21.0
^ permalink raw reply related
* [PATCH v4 09/17] fscrypt: add FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add a new fscrypt ioctl, FS_IOC_GET_ENCRYPTION_KEY_STATUS. Given a key
specified by 'struct fscrypt_key_specifier' (the same way a key is
specified for the other fscrypt key management ioctls), it returns
status information in a 'struct fscrypt_get_key_status_arg'.
The main motivation for this is that applications need to be able to
check whether an encrypted directory is "unlocked" or not, so that they
can add the key if it is not, and avoid adding the key (which may
involve prompting the user for a passphrase) if it already is.
It's possible to use some workarounds such as checking whether opening a
regular file fails with ENOKEY, or checking whether the filenames "look
like gibberish" or not. However, no workaround is usable in all cases.
Like the other key management ioctls, the keyrings syscalls may seem at
first to be a good fit for this. Unfortunately, they are not. Even if
we exposed the keyring ID of the ->s_master_keys keyring and gave
everyone Search permission on it (note: currently the keyrings
permission system would also allow everyone to "invalidate" the keyring
too), the fscrypt keys have an additional state that doesn't map cleanly
to the keyrings API: the secret can be removed, but we can be still
tracking the files that were using the key, and the removal can be
re-attempted or the secret added again.
After later patches, some applications will also need a way to determine
whether a key was added by the current user vs. by some other user.
Reserved fields are included in fscrypt_get_key_status_arg for this and
other future extensions.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/keyring.c | 60 ++++++++++++++++++++++++++++++++++++
include/linux/fscrypt.h | 7 +++++
include/uapi/linux/fscrypt.h | 15 +++++++++
3 files changed, 82 insertions(+)
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index ee9be002c1490..08e14497c8e30 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -11,6 +11,7 @@
*
* - FS_IOC_ADD_ENCRYPTION_KEY: add a key
* - FS_IOC_REMOVE_ENCRYPTION_KEY: remove a key
+ * - FS_IOC_GET_ENCRYPTION_KEY_STATUS: get key status
*/
#include <linux/key-type.h>
@@ -482,6 +483,65 @@ int fscrypt_ioctl_remove_key(struct file *filp, const void __user *uarg)
}
EXPORT_SYMBOL_GPL(fscrypt_ioctl_remove_key);
+/*
+ * Retrieve the status of an fscrypt master encryption key.
+ *
+ * We set ->status to indicate whether the key is absent, present, or
+ * incompletely removed. "Incompletely removed" means that the master key
+ * secret has been removed, but some files which had been unlocked with it are
+ * still in use. This field allows applications to easily determine the state
+ * of an encrypted directory without using a hack such as trying to open a
+ * regular file in it (which can confuse the "incompletely removed" state with
+ * absent or present).
+ */
+int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg)
+{
+ struct super_block *sb = file_inode(filp)->i_sb;
+ struct fscrypt_get_key_status_arg arg;
+ struct key *key;
+ struct fscrypt_master_key *mk;
+ int err;
+
+ if (copy_from_user(&arg, uarg, sizeof(arg)))
+ return -EFAULT;
+
+ if (!valid_key_spec(&arg.key_spec))
+ return -EINVAL;
+
+ if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
+ return -EINVAL;
+
+ memset(arg.__out_reserved, 0, sizeof(arg.__out_reserved));
+
+ key = fscrypt_find_master_key(sb, &arg.key_spec);
+ if (IS_ERR(key)) {
+ if (key != ERR_PTR(-ENOKEY))
+ return PTR_ERR(key);
+ arg.status = FSCRYPT_KEY_STATUS_ABSENT;
+ err = 0;
+ goto out;
+ }
+ mk = key->payload.data[0];
+ down_read(&key->sem);
+
+ if (!is_master_key_secret_present(&mk->mk_secret)) {
+ arg.status = FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED;
+ err = 0;
+ goto out_release_key;
+ }
+
+ arg.status = FSCRYPT_KEY_STATUS_PRESENT;
+ err = 0;
+out_release_key:
+ up_read(&key->sem);
+ key_put(key);
+out:
+ if (!err && copy_to_user(uarg, &arg, sizeof(arg)))
+ err = -EFAULT;
+ return err;
+}
+EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_key_status);
+
int __init fscrypt_init_keyring(void)
{
return register_key_type(&key_type_fscrypt);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index b6f8832dadba4..5f251cad255b8 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -116,6 +116,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *,
extern void fscrypt_sb_free(struct super_block *sb);
extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
extern int fscrypt_ioctl_remove_key(struct file *filp, const void __user *arg);
+extern int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
/* keysetup.c */
extern int fscrypt_get_encryption_info(struct inode *);
@@ -335,6 +336,12 @@ static inline int fscrypt_ioctl_remove_key(struct file *filp,
return -EOPNOTSUPP;
}
+static inline int fscrypt_ioctl_get_key_status(struct file *filp,
+ void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
/* keysetup.c */
static inline int fscrypt_get_encryption_info(struct inode *inode)
{
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index 3302a407131e6..042e70a4ff7ee 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -71,11 +71,26 @@ struct fscrypt_remove_key_arg {
__u32 __reserved[6];
};
+/* Struct passed to FS_IOC_GET_ENCRYPTION_KEY_STATUS */
+struct fscrypt_get_key_status_arg {
+ /* input */
+ struct fscrypt_key_specifier key_spec;
+ __u32 __reserved[6];
+
+ /* output */
+#define FSCRYPT_KEY_STATUS_ABSENT 1
+#define FSCRYPT_KEY_STATUS_PRESENT 2
+#define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3
+ __u32 status;
+ __u32 __out_reserved[15];
+};
+
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
#define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct fscrypt_add_key_arg)
#define FS_IOC_REMOVE_ENCRYPTION_KEY _IOW('f', 24, struct fscrypt_remove_key_arg)
+#define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 25, struct fscrypt_get_key_status_arg)
/**********************************************************************/
--
2.21.0
^ permalink raw reply related
* [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation
From: Eric Biggers @ 2019-04-02 15:45 UTC (permalink / raw)
To: linux-fscrypt
Cc: Satya Tangirala, linux-api, linux-f2fs-devel, keyrings, linux-mtd,
linux-crypto, linux-fsdevel, linux-ext4, Paul Crowley
In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org>
From: Eric Biggers <ebiggers@google.com>
Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of
deriving additional key material from the fscrypt master keys for v2
encryption policies. HKDF is a key derivation function built on top of
HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an
"hmac(sha512)" transform allocated from the crypto API.
We'll be using this to replace the AES-ECB based KDF currently used to
derive the per-file encryption keys. While the AES-ECB based KDF is
believed to meet the original security requirements, it is nonstandard
and has problems that don't exist in modern KDFs such as HKDF:
1. It's reversible. Given a derived key and nonce, an attacker can
easily compute the master key. This is okay if the master key and
derived keys are equally hard to compromise, but now we'd like to be
more robust against threats such as a derived key being compromised
through a timing attack, or a derived key for an in-use file being
compromised after the master key has already been removed.
2. It doesn't evenly distribute the entropy from the master key; each 16
input bytes only affects the corresponding 16 output bytes.
3. It isn't easily extensible to deriving other values or keys that are
or will be needed, such as per-mode keys (which is what DIRECT_KEY
policies really should use, rather than the master key directly as
they do now) or a public hash for securely identifying the key (to
ensure that an encrypted file cannot be set up with the wrong key).
HKDF solves all the above problems.
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
fs/crypto/Kconfig | 2 +
fs/crypto/Makefile | 1 +
fs/crypto/fscrypt_private.h | 15 +++
fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++
4 files changed, 206 insertions(+)
create mode 100644 fs/crypto/hkdf.c
diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig
index f0de238000c02..c160598a9fe2c 100644
--- a/fs/crypto/Kconfig
+++ b/fs/crypto/Kconfig
@@ -7,6 +7,8 @@ config FS_ENCRYPTION
select CRYPTO_XTS
select CRYPTO_CTS
select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select CRYPTO_HMAC
select KEYS
help
Enable encryption of files and directories. This
diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
index accdd622c9083..4977b43479289 100644
--- a/fs/crypto/Makefile
+++ b/fs/crypto/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
fscrypto-y := crypto.o \
fname.o \
+ hkdf.o \
hooks.o \
keyring.o \
keysetup.o \
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index cc1862c9baa1d..e2a65189eb578 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
u32 orig_len, u32 max_len,
u32 *encrypted_len_ret);
+/* hkdf.c */
+
+struct fscrypt_hkdf {
+ struct crypto_shash *hmac_tfm;
+};
+
+extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
+ unsigned int master_key_size);
+
+extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context,
+ const u8 *info, unsigned int infolen,
+ u8 *okm, unsigned int okmlen);
+
+extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
+
/* keyring.c */
/*
diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c
new file mode 100644
index 0000000000000..3f9dbf171d967
--- /dev/null
+++ b/fs/crypto/hkdf.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation
+ * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010):
+ * "Cryptographic Extraction and Key Derivation: The HKDF Scheme".
+ *
+ * This is used to derive keys from the fscrypt master keys.
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+
+#include "fscrypt_private.h"
+
+/*
+ * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses
+ * SHA-512 because it is reasonably secure and efficient; and since it produces
+ * a 64-byte digest, deriving an AES-256-XTS key preserves all 64 bytes of
+ * entropy from the master key and requires only one iteration of HKDF-Expand.
+ */
+#define HKDF_HMAC_ALG "hmac(sha512)"
+#define HKDF_HASHLEN SHA512_DIGEST_SIZE
+
+/*
+ * HKDF consists of two steps:
+ *
+ * 1. HKDF-Extract: extract a pseudorandom key of length HKDF_HASHLEN bytes from
+ * the input keying material and optional salt.
+ * 2. HKDF-Expand: expand the pseudorandom key into output keying material of
+ * any length, parameterized by an application-specific info string.
+ *
+ * HKDF-Extract can be skipped if the input is already a pseudorandom key of
+ * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take
+ * shorter keys, and we don't want to force users of those modes to generate
+ * unnecessarily long keys. Thus fscrypt still does HKDF-Extract.
+ *
+ * HKDF-Extract also supports a salt. Choosing a random salt per input key
+ * would permit the input key to come from *any* source with sufficient entropy,
+ * even if it's not distributed uniformly. However, fscrypt doesn't take
+ * advantage of this because userspace should already provide good pseudorandom
+ * keys, which makes this unnecessary; also, having to persist a random salt per
+ * key from kernel mode would pose significant implementation complexity. Thus
+ * fscrypt uses a fixed salt. But to be slightly more robust against userspace
+ * (unwisely) reusing the fscrypt keys for another purpose, and to force
+ * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses
+ * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869.
+ */
+
+#define HKDF_SALT "fscrypt_hkdf_salt"
+#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT)
+
+/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */
+static int hkdf_extract(struct crypto_shash *hmac_tfm, const u8 *ikm,
+ unsigned int ikmlen, u8 prk[HKDF_HASHLEN])
+{
+ SHASH_DESC_ON_STACK(desc, hmac_tfm);
+ int err;
+
+ err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN);
+ if (err)
+ return err;
+
+ desc->tfm = hmac_tfm;
+ desc->flags = 0;
+ err = crypto_shash_digest(desc, ikm, ikmlen, prk);
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * Compute HKDF-Extract using the given master key as the input keying material,
+ * and prepare an HMAC transform object keyed by the resulting pseudorandom key.
+ *
+ * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many
+ * times without having to recompute HKDF-Extract each time.
+ */
+int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
+ unsigned int master_key_size)
+{
+ struct crypto_shash *hmac_tfm;
+ u8 prk[HKDF_HASHLEN];
+ int err;
+
+ hmac_tfm = crypto_alloc_shash(HKDF_HMAC_ALG, 0, 0);
+ if (IS_ERR(hmac_tfm)) {
+ fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld",
+ PTR_ERR(hmac_tfm));
+ return PTR_ERR(hmac_tfm);
+ }
+
+ BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk));
+
+ err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk);
+ if (err)
+ goto err_free_tfm;
+
+ err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk));
+ if (err)
+ goto err_free_tfm;
+
+ hkdf->hmac_tfm = hmac_tfm;
+ goto out;
+
+err_free_tfm:
+ crypto_free_shash(hmac_tfm);
+out:
+ memzero_explicit(prk, sizeof(prk));
+ return err;
+}
+
+/*
+ * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which
+ * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen'
+ * bytes of output keying material parameterized by the application-specific
+ * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is
+ * thread-safe and may be called by multiple threads in parallel.
+ *
+ * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt
+ * adds to its application-specific info strings to guarantee that it doesn't
+ * accidentally repeat an info string when using HKDF for different purposes.)
+ */
+int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context,
+ const u8 *info, unsigned int infolen,
+ u8 *okm, unsigned int okmlen)
+{
+ SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm);
+ unsigned int i;
+ int err;
+ const u8 *prev = NULL;
+ u8 counter = 1;
+ u8 tmp[HKDF_HASHLEN];
+
+ if (WARN_ON(okmlen > 255 * HKDF_HASHLEN))
+ return -EINVAL;
+
+ desc->tfm = hkdf->hmac_tfm;
+ desc->flags = 0;
+
+ for (i = 0; i < okmlen; i += HKDF_HASHLEN) {
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, HKDF_HASHLEN);
+ if (err)
+ goto out;
+ }
+
+ BUILD_BUG_ON(sizeof(context) != 1);
+ err = crypto_shash_update(desc, &context, 1);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+
+ BUILD_BUG_ON(sizeof(counter) != 1);
+ if (okmlen - i < HKDF_HASHLEN) {
+ err = crypto_shash_finup(desc, &counter, 1, tmp);
+ if (err)
+ goto out;
+ memcpy(&okm[i], tmp, okmlen - i);
+ memzero_explicit(tmp, sizeof(tmp));
+ } else {
+ err = crypto_shash_finup(desc, &counter, 1, &okm[i]);
+ if (err)
+ goto out;
+ }
+ counter++;
+ prev = &okm[i];
+ }
+ err = 0;
+out:
+ if (unlikely(err))
+ memzero_explicit(okm, okmlen); /* so caller doesn't need to */
+ shash_desc_zero(desc);
+ return err;
+}
+
+void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf)
+{
+ crypto_free_shash(hkdf->hmac_tfm);
+}
--
2.21.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox