Re: [PATCH ghak90 V8 04/16] audit: convert to contid list to check for orch/engine ownership

From: Paul Moore
Date: Wed Feb 05 2020 - 17:40:53 EST


On Tue, Feb 4, 2020 at 5:52 PM Richard Guy Briggs <rgb@xxxxxxxxxx> wrote:
> On 2020-01-22 16:28, Paul Moore wrote:
> > On Tue, Dec 31, 2019 at 2:50 PM Richard Guy Briggs <rgb@xxxxxxxxxx> wrote:
> > >
> > > Store the audit container identifier in a refcounted kernel object that
> > > is added to the master list of audit container identifiers. This will
> > > allow multiple container orchestrators/engines to work on the same
> > > machine without danger of inadvertantly re-using an existing identifier.
> > > It will also allow an orchestrator to inject a process into an existing
> > > container by checking if the original container owner is the one
> > > injecting the task. A hash table list is used to optimize searches.
> > >
> > > Signed-off-by: Richard Guy Briggs <rgb@xxxxxxxxxx>
> > > ---
> > > include/linux/audit.h | 14 ++++++--
> > > kernel/audit.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++---
> > > kernel/audit.h | 8 +++++
> > > 3 files changed, 112 insertions(+), 8 deletions(-)

...

> > > @@ -232,7 +263,11 @@ int audit_alloc(struct task_struct *tsk)
> > > }
> > > info->loginuid = audit_get_loginuid(current);
> > > info->sessionid = audit_get_sessionid(current);
> > > - info->contid = audit_get_contid(current);
> > > + spin_lock(&audit_contobj_list_lock);
> > > + info->cont = _audit_contobj(current);
> > > + if (info->cont)
> > > + _audit_contobj_hold(info->cont);
> > > + spin_unlock(&audit_contobj_list_lock);
> >
> > If we are taking a spinlock in order to bump the refcount, does it
> > really need to be a refcount_t or can we just use a normal integer?
> > In RCU protected lists a spinlock is usually used to protect
> > adds/removes to the list, not the content of individual list items.
> >
> > My guess is you probably want to use the spinlock as described above
> > (list add/remove protection) and manipulate the refcount_t inside a
> > RCU read lock protected region.
>
> Ok, I guess it could be an integer if it were protected by the spinlock,
> but I think you've guessed my intent, so let us keep it as a refcount
> and tighten the spinlock scope and use rcu read locking to protect _get
> and _put in _alloc, _free, and later on when protecting the network
> namespace contobj lists. This should reduce potential contention for
> the spinlock to one location over fewer lines of code in that place
> while speeding up updates and slightly simplifying code in the others.

If it helps, you should be able to find plenty of rcu/spinlock
protected list examples in the kernel code. It might be a good idea
if you spent some time looking at those implementations first to get
an idea of how it is usually done.

> > > @@ -2381,9 +2425,12 @@ int audit_set_contid(struct task_struct *task, u64 contid)
> > > }
> > > oldcontid = audit_get_contid(task);
> > > read_lock(&tasklist_lock);
> > > - /* Don't allow the audit containerid to be unset */
> > > + /* Don't allow the contid to be unset */
> > > if (!audit_contid_valid(contid))
> > > rc = -EINVAL;
> > > + /* Don't allow the contid to be set to the same value again */
> > > + else if (contid == oldcontid) {
> > > + rc = -EADDRINUSE;
> >
> > First, is that brace a typo? It looks like it. Did this compile?
>
> Yes, it was fixed in the later patch that restructured the if
> statements.

Generic reminder that each patch should compile and function on it's
own without the need for any follow-up patches. I know Richard is
already aware of that, and this was a mistake that slipped through the
cracks; this reminder is more for those who may be lurking on the
list.

> > Second, can you explain why (re)setting the audit container ID to the
> > same value is something we need to prohibit? I'm guessing it has
> > something to do with explicitly set vs inherited, but I don't want to
> > assume too much about your thinking behind this.
>
> It made the refcounting more complicated later, and besides, the
> prohibition on setting the contid again if it is already set would catch
> this case, so I'll remove it in this patch and ensure this action
> doesn't cause a problem in later patches.
>
> > If it is "set vs inherited", would allowing an orchestrator to
> > explicitly "set" an inherited audit container ID provide some level or
> > protection against moving the task?
>
> I can't see it helping prevent this since later descendancy checks will
> stop this move anyways.

That's what I thought, but I was just trying to think of any reason
why you felt this might have been useful since it was in the patch.
If it's in the patch I tend to fall back on the idea that it must have
served a purpose ;)

> > > @@ -2396,8 +2443,49 @@ int audit_set_contid(struct task_struct *task, u64 contid)
> > > else if (audit_contid_set(task))
> > > rc = -ECHILD;
> > > read_unlock(&tasklist_lock);
> > > - if (!rc)
> > > - task->audit->contid = contid;
> > > + if (!rc) {
> > > + struct audit_contobj *oldcont = _audit_contobj(task);
> > > + struct audit_contobj *cont = NULL, *newcont = NULL;
> > > + int h = audit_hash_contid(contid);
> > > +
> > > + rcu_read_lock();
> > > + list_for_each_entry_rcu(cont, &audit_contid_hash[h], list)
> > > + if (cont->id == contid) {
> > > + /* task injection to existing container */
> > > + if (current == cont->owner) {
> >
> > Do we have any protection against the task pointed to by cont->owner
> > going away and a new task with the same current pointer value (no
> > longer the legitimate audit container ID owner) manipulating the
> > target task's audit container ID?
>
> Yes, the get_task_struct() call below.

Gotcha.

--
paul moore
www.paul-moore.com