* Re: [PATCH V31 25/25] debugfs: Disable open() when kernel is locked down
From: Andy Lutomirski @ 2019-03-27 17:39 UTC (permalink / raw)
To: Greg KH
Cc: Andy Lutomirski, Matthew Garrett, James Morris, LSM List, LKML,
David Howells, Linux API, Matthew Garrett
In-Reply-To: <20190327053342.GA17484@kroah.com>
On Tue, Mar 26, 2019 at 10:33 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Tue, Mar 26, 2019 at 10:29:41PM -0700, Andy Lutomirski wrote:
> >
> >
> > > On Mar 26, 2019, at 10:06 PM, Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > >> On Tue, Mar 26, 2019 at 09:29:14PM -0700, Andy Lutomirski wrote:
> > >>> On Tue, Mar 26, 2019 at 5:31 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >>>
> > >>>> On Tue, Mar 26, 2019 at 12:20:24PM -0700, Andy Lutomirski wrote:
> > >>>> On Tue, Mar 26, 2019 at 11:28 AM Matthew Garrett
> > >>>> <matthewgarrett@google.com> wrote:
> > >>>>>
> > >>>>> From: Matthew Garrett <mjg59@google.com>
> > >>>>>
> > >>>>> debugfs has not been meaningfully audited in terms of ensuring that
> > >>>>> userland cannot trample over the kernel. At Greg's request, disable
> > >>>>> access to it entirely when the kernel is locked down. This is done at
> > >>>>> open() time rather than init time as the kernel lockdown status may be
> > >>>>> made stricter at runtime.
> > >>>>
> > >>>> Ugh. Some of those files are very useful. Could this perhaps still
> > >>>> allow O_RDONLY if we're in INTEGRITY mode?
> > >>>
> > >>> Useful for what? Debugging, sure, but for "normal operation", no kernel
> > >>> functionality should ever require debugfs. If it does, that's a bug and
> > >>> should be fixed.
> > >>>
> > >>
> > >> I semi-regularly read files in debugfs to diagnose things, and I think
> > >> it would be good for this to work on distro kernels.
> > >
> > > Doing that for debugging is wonderful. People who want this type of
> > > "lock down" are trading potential security for diagnositic ability.
> > >
> >
> > I think you may be missing the point of splitting lockdown to separate integrity and confidentiality. Can you actually think of a case where *reading* a debugfs file can take over a kernel?
>
> Reading a debugfs file can expose loads of things that can help take
> over a kernel, or at least make it easier. Pointer addresses, internal
> system state, loads of other fun things. And before 4.14 or so, it was
> pretty trivial to use it to oops the kernel as well (not an issue here
> anymore, but people are right to be nervous).
>
> Personally, I think these are all just "confidentiality" type things,
> but who really knows given the wild-west nature of debugfs (which is as
> designed). And given that I think this patch series just crazy anyway,
> I really don't care :)
>
As far as I'm concerned, preventing root from crashing the system
should not be a design goal of lockdown at all. And I think that the
"integrity" mode should be as non-annoying as possible, so I think we
should allow reading from debugfs.
^ permalink raw reply
* Re: [PATCH V31 25/25] debugfs: Disable open() when kernel is locked down
From: Matthew Garrett @ 2019-03-27 17:42 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Greg KH, James Morris, LSM List, LKML, David Howells, Linux API
In-Reply-To: <CALCETrUW=r=xyr4pGiGnn9zUEevf=z6PP3111Ds6qguV5SjYUQ@mail.gmail.com>
On Wed, Mar 27, 2019 at 10:40 AM Andy Lutomirski <luto@kernel.org> wrote:
> As far as I'm concerned, preventing root from crashing the system
> should not be a design goal of lockdown at all. And I think that the
> "integrity" mode should be as non-annoying as possible, so I think we
> should allow reading from debugfs.
I have no horse in this game - I'm happy to bring back the previous
approach for integrity mode and block reads entirely in
confidentiality mode, but I'd rather not spend another release cycle
arguing about it.
^ permalink raw reply
* Re: [PATCH 16/32] x86/vdso: Generate vdso{,32}-timens.lds
From: Andrei Vagin @ 2019-03-27 18:00 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Rasmus Villemoes, Dmitry Safonov, LKML, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Andy Tucker, Arnd Bergmann,
Christian Brauner, Cyrill Gorcunov, Dmitry Safonov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, containers, criu,
linux-api, x86
In-Reply-To: <alpine.DEB.2.21.1902081051410.1645@nanos.tec.linutronix.de>
While the generic vdso patchset is in development, we decided to think
about what other ways of generating two vdso libraries. In this
patchset, we use a linker script, but it looks too complicated, so we
decided to look at other options. Another obvious approach is the code
patching technique. The main idea was to reduce the amount of
arch-dependent code and Dmitry brought with the idea of three labels.
Let’s look at this pseudo-code:
Int vdso_clock_gettime(clockid_t clk, struct timespec *ts)
{
...
l_call:
clk_to_ns(clk, ts)
l_return:
return 0;
annotate_reachable();
l_out:
nop();
return 0;
}
Here we can see three labels. Without patching this code, the function
will apply vdso offsets. But if we copy the code between the last two
labels to the first label, we will get a version which skips vdso
offsets. The patch which implements this idea will be in replies to this
email. It was tested on x86_64 and with gcc as a compiler, but we
suspect that there might be some issues on other architectures or with
other compilers. So we would like to ask the help of the community to
understand what we have to do to be sure that this code works always
correctly.
The second patch implements static_branch for the vdso code.
Here are only a few lines of arch-dependent code:
+static __always_inline bool timens_static_branch(void)
+{
+ asm_volatile_goto("1:\n\t"
+ ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t"
+ ".pushsection __retcall_table, \"aw\"\n\t"
+ "2: .word 1b - 2b, %l[l_yes] - 2b\n\t"
+ ".popsection\n\t"
+ : : : : l_yes);
+
+ return false;
+l_yes:
+ return true;
+}
This is a slightly modified version of the arch_static_branch()
function. The timens code in vdso looks like this:
if (timens_static_branch()) {
clk_to_ns(clk, ts);
}
The version of vdso which is compiled from sources will never execute
clk_to_ns(). And then we can patch the 'no-op' in the straight-line
codepath with a 'jump' instruction to the out-of-line true branch and
get the timens version of the vdso library.
Now we can compare these three versions. Our opinion is that the version
with three labels looks cleaner and if it will work with all compilers
on all architectures, we probably have to choose it. Otherwise, we would
prefer the version with static_branches, because it is simpler than the
version with the linker script.
Thanks,
Andrei
On Fri, Feb 08, 2019 at 10:57:57AM +0100, Thomas Gleixner wrote:
> On Thu, 7 Feb 2019, Rasmus Villemoes wrote:
>
> Cc: + Vincenzo, Will
>
> > On 06/02/2019 01.10, Dmitry Safonov wrote:
> > > As it has been discussed on timens RFC, adding a new conditional branch
> > > `if (inside_time_ns)` on VDSO for all processes is undesirable.
> > > It will add a penalty for everybody as branch predictor may mispredict
> > > the jump. Also there are instruction cache lines wasted on cmp/jmp.
> > >
> > > Those effects of introducing time namespace are very much unwanted
> > > having in mind how much work have been spent on micro-optimisation
> > > vdso code.
> > >
> > > Addressing those problems, there are two versions of VDSO's .so:
> > > for host tasks (without any penalty) and for processes inside of time
> > > namespace with clk_to_ns() that subtracts offsets from host's time.
> > >
> > > Unfortunately, to allow changing VDSO VMA on a running process,
> > > the entry points to VDSO should have the same offsets (addresses).
> > > That's needed as i.e. application that calls setns() may have already
> > > resolved VDSO symbols in GOT/PLT.
> >
> > These (14-19, if I'm reading them right) seems to add quite a lot of
> > complexity and fragility to the build, and other architectures would
> > probably have to add something similar to their vdso builds.
>
> Yes and we really want to avoid that. The VDSO implementations are
> pointlessly different accross the architectures and there is effort on the
> way to consolidate them:
>
> https://lkml.kernel.org/r/20190115135539.24762-1-vincenzo.frascino@arm.com
>
> I talked to Vincenzo earlier this week and he's working on a new version of
> that. The timens stuff wants to go on top of the consolidation otherwise we
> end up with another set of pointlessly different and differently broken
> VDSO variants.
>
> Thanks,
>
> tglx
^ permalink raw reply
* [PATCH RFC] x86/asm: Introduce static_retcall(s)
From: Andrei Vagin @ 2019-03-27 18:06 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Dmitry Safonov, Rasmus Villemoes, LKML, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Andy Tucker, Arnd Bergmann,
Christian Brauner, Cyrill Gorcunov, Dmitry Safonov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, containers, criu,
linux-api, x86
In-Reply-To: <20190327175957.GA9309@gmail.com>
From: Dmitry Safonov <dima@arista.com>
Provide framework to overwrite tail call in a function with return.
XXX: split vdso/generic part
Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
---
arch/x86/entry/vdso/vclock_gettime.c | 19 ++++++----
arch/x86/entry/vdso/vdso-layout.lds.S | 1 +
arch/x86/entry/vdso/vdso2c.h | 11 +++++-
arch/x86/entry/vdso/vma.c | 22 +++++++++++
arch/x86/include/asm/static_retcall.h | 54 +++++++++++++++++++++++++++
arch/x86/include/asm/vdso.h | 1 +
6 files changed, 99 insertions(+), 9 deletions(-)
create mode 100644 arch/x86/include/asm/static_retcall.h
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index cb55bd994497..9416f1ee6b73 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -18,6 +18,7 @@
#include <asm/msr.h>
#include <asm/pvclock.h>
#include <asm/mshyperv.h>
+#include <asm/static_retcall.h>
#include <linux/math64.h>
#include <linux/time.h>
#include <linux/kernel.h>
@@ -39,7 +40,7 @@ extern u8 hvclock_page
__attribute__((visibility("hidden")));
#endif
-#ifdef BUILD_VDSO_TIME_NS
+#ifdef CONFIG_TIME_NS
extern u8 timens_page
__attribute__((visibility("hidden")));
#endif
@@ -145,9 +146,9 @@ notrace static inline u64 vgetcyc(int mode)
return U64_MAX;
}
+#ifdef CONFIG_TIME_NS
notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts)
{
-#ifdef BUILD_VDSO_TIME_NS
struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
struct timespec64 *offset64;
@@ -173,9 +174,13 @@ notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts
ts->tv_nsec += NSEC_PER_SEC;
ts->tv_sec--;
}
-
-#endif
}
+#define _static_retcall static_retcall
+#define _static_retcall_int static_retcall_int
+#else
+#define _static_retcall(...)
+#define _static_retcall_int(...)
+#endif
notrace static int do_hres(clockid_t clk, struct timespec *ts)
{
@@ -203,9 +208,7 @@ notrace static int do_hres(clockid_t clk, struct timespec *ts)
ts->tv_sec = sec + __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
- clk_to_ns(clk, ts);
-
- return 0;
+ _static_retcall_int(0, clk_to_ns, clk, ts);
}
notrace static void do_coarse(clockid_t clk, struct timespec *ts)
@@ -219,7 +222,7 @@ notrace static void do_coarse(clockid_t clk, struct timespec *ts)
ts->tv_nsec = base->nsec;
} while (unlikely(gtod_read_retry(gtod, seq)));
- clk_to_ns(clk, ts);
+ _static_retcall(clk_to_ns, clk, ts);
}
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index ba216527e59f..075cae6f33bf 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -45,6 +45,7 @@ SECTIONS
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
+ __retcall_table : { *(__retcall_table) } :text
.dynamic : { *(.dynamic) } :text :dynamic
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index 660f725a02c1..ae91567fd567 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -16,7 +16,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
unsigned int i, syms_nr;
unsigned long j;
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
- *alt_sec = NULL;
+ *alt_sec = NULL, *retcall_sec = NULL;
ELF(Dyn) *dyn = 0, *dyn_end = 0;
const char *secstrings;
INT_BITS syms[NSYMS] = {};
@@ -78,6 +78,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
if (!strcmp(secstrings + GET_LE(&sh->sh_name),
".altinstructions"))
alt_sec = sh;
+ if (!strcmp(secstrings + GET_LE(&sh->sh_name),
+ "__retcall_table"))
+ retcall_sec = sh;
}
if (!symtab_hdr)
@@ -165,6 +168,12 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile, "\t.alt_len = %lu,\n",
(unsigned long)GET_LE(&alt_sec->sh_size));
}
+ if (retcall_sec) {
+ fprintf(outfile, "\t.retcall = %lu,\n",
+ (unsigned long)GET_LE(&retcall_sec->sh_offset));
+ fprintf(outfile, "\t.retcall_len = %lu,\n",
+ (unsigned long)GET_LE(&retcall_sec->sh_size));
+ }
for (i = 0; i < NSYMS; i++) {
if (required_syms[i].export && syms[i])
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 0b8d9f6f0ce3..b4ea7a2ebfed 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -25,6 +25,7 @@
#include <asm/cpufeature.h>
#include <asm/mshyperv.h>
#include <asm/page.h>
+#include <asm/static_retcall.h>
#include <asm/tlb.h>
#if defined(CONFIG_X86_64)
@@ -38,6 +39,25 @@ static __init int vdso_setup(char *s)
__setup("vdso=", vdso_setup);
#endif
+static __init int apply_retcalls(struct retcall_entry *ent, unsigned long nr)
+{
+ while (nr--) {
+ void *call_addr = (void *)ent + ent->call;
+ void *ret_addr = (void *)ent + ent->ret;
+ size_t ret_sz = ent->out - ent->ret;
+
+ if (WARN_ON(ret_sz > PAGE_SIZE))
+ goto next;
+
+ memcpy(call_addr, ret_addr, ret_sz);
+
+next:
+ ent++;
+ }
+
+ return 0;
+}
+
void __init init_vdso_image(struct vdso_image *image)
{
BUG_ON(image->size % PAGE_SIZE != 0);
@@ -51,6 +71,8 @@ void __init init_vdso_image(struct vdso_image *image)
return;
memcpy(image->text_timens, image->text, image->size);
+ apply_retcalls((struct retcall_entry *)(image->text + image->retcall),
+ image->retcall_len / sizeof(struct retcall_entry));
#endif
}
diff --git a/arch/x86/include/asm/static_retcall.h b/arch/x86/include/asm/static_retcall.h
new file mode 100644
index 000000000000..fdb13795b74d
--- /dev/null
+++ b/arch/x86/include/asm/static_retcall.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Dmitry Safonov, Andrey Vagin
+ */
+
+#ifndef _ASM_X86_STATIC_RETCALL_H
+#define _ASM_X86_STATIC_RETCALL_H
+
+struct retcall_entry {
+ u16 call;
+ u16 ret;
+ u16 out;
+};
+
+#define static_retcall(func, ...) \
+ do { \
+ asm_volatile_goto( \
+ ".pushsection __retcall_table, \"aw\" \n\t" \
+ "2: .word %l[l_call] - 2b\n\t" \
+ ".word %l[l_return] - 2b\n\t" \
+ ".word %l[l_out] - 2b\n\t" \
+ ".popsection" \
+ : : : : l_call, l_return, l_out); \
+l_call: \
+ func(__VA_ARGS__); \
+l_return: \
+ return; \
+ annotate_reachable(); \
+l_out: \
+ nop(); \
+ return; \
+ } while(0)
+
+#define static_retcall_int(ret, func, ...) \
+ do { \
+ asm_volatile_goto( \
+ ".pushsection __retcall_table, \"aw\" \n\t" \
+ _ASM_ALIGN "\n\t" \
+ "2: .word %l[l_call] - 2b\n\t" \
+ ".word %l[l_return] - 2b\n\t" \
+ ".word %l[l_out] - 2b\n\t" \
+ ".popsection" \
+ : : : : l_call, l_return, l_out); \
+l_call: \
+ func(__VA_ARGS__); \
+l_return: \
+ return ret; \
+ annotate_reachable(); \
+l_out: \
+ nop(); \
+ return ret; \
+ } while(0)
+
+#endif
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 583133446874..acdf70bf814b 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -16,6 +16,7 @@ struct vdso_image {
unsigned long size; /* Always a multiple of PAGE_SIZE */
unsigned long alt, alt_len;
+ unsigned long retcall, retcall_len;
long sym_vvar_start; /* Negative offset to the vvar area */
--
2.20.1
^ permalink raw reply related
* [PATCH RFC] vdso: introduce timens_static_branch
From: Andrei Vagin @ 2019-03-27 18:06 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Andrei Vagin, Rasmus Villemoes, Dmitry Safonov, LKML,
Adrian Reber, Andrei Vagin, Andy Lutomirski, Andy Tucker,
Arnd Bergmann, Christian Brauner, Cyrill Gorcunov, Dmitry Safonov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, containers, criu,
linux-a
In-Reply-To: <20190327175957.GA9309@gmail.com>
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
Addressing those problems, there are two versions of VDSO's .so:
for host tasks (without any penalty) and for processes inside of time
namespace with clk_to_ns() that subtracts offsets from host's time.
This patch introduces timens_static_branch(), which is similar with
static_branch_unlikely.
The timens code in vdso looks like this:
if (timens_static_branch()) {
clk_to_ns(clk, ts);
}
The version of vdso which is compiled from sources will never execute
clk_to_ns(). And then we can patch the 'no-op' in the straight-line
codepath with a 'jump' instruction to the out-of-line true branch and
get the timens version of the vdso library.
Cc: Dmitry Safonov <dima@arista.com>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
---
arch/x86/entry/vdso/vclock_gettime.c | 21 ++++++++++++++-------
arch/x86/entry/vdso/vdso-layout.lds.S | 1 +
arch/x86/entry/vdso/vdso2c.h | 11 ++++++++++-
arch/x86/entry/vdso/vma.c | 19 +++++++++++++++++++
arch/x86/include/asm/jump_label.h | 14 ++++++++++++++
arch/x86/include/asm/vdso.h | 1 +
include/linux/jump_label.h | 5 +++++
7 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index cb55bd994497..74de42f1f7d8 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -18,6 +18,7 @@
#include <asm/msr.h>
#include <asm/pvclock.h>
#include <asm/mshyperv.h>
+#include <asm/jump_label.h>
#include <linux/math64.h>
#include <linux/time.h>
#include <linux/kernel.h>
@@ -39,7 +40,7 @@ extern u8 hvclock_page
__attribute__((visibility("hidden")));
#endif
-#ifdef BUILD_VDSO_TIME_NS
+#ifdef CONFIG_TIME_NS
extern u8 timens_page
__attribute__((visibility("hidden")));
#endif
@@ -145,9 +146,9 @@ notrace static inline u64 vgetcyc(int mode)
return U64_MAX;
}
+#ifdef CONFIG_TIME_NS
notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts)
{
-#ifdef BUILD_VDSO_TIME_NS
struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
struct timespec64 *offset64;
@@ -173,9 +174,12 @@ notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts
ts->tv_nsec += NSEC_PER_SEC;
ts->tv_sec--;
}
-
-#endif
}
+#define _timens_static_branch_unlikely timens_static_branch_unlikely
+#else
+notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts) {}
+notrace static __always_inline bool _timens_static_branch_unlikely(void) { return false; }
+#endif
notrace static int do_hres(clockid_t clk, struct timespec *ts)
{
@@ -203,8 +207,9 @@ notrace static int do_hres(clockid_t clk, struct timespec *ts)
ts->tv_sec = sec + __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
- clk_to_ns(clk, ts);
-
+ if (_timens_static_branch_unlikely()) {
+ clk_to_ns(clk, ts);
+ }
return 0;
}
@@ -219,7 +224,9 @@ notrace static void do_coarse(clockid_t clk, struct timespec *ts)
ts->tv_nsec = base->nsec;
} while (unlikely(gtod_read_retry(gtod, seq)));
- clk_to_ns(clk, ts);
+ if (_timens_static_branch_unlikely()) {
+ clk_to_ns(clk, ts);
+ }
}
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index ba216527e59f..69dbe4821aa5 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -45,6 +45,7 @@ SECTIONS
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
+ __jump_table : { *(__jump_table) } :text
.dynamic : { *(.dynamic) } :text :dynamic
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index 660f725a02c1..e4eef5e1c6ac 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -16,7 +16,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
unsigned int i, syms_nr;
unsigned long j;
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
- *alt_sec = NULL;
+ *alt_sec = NULL, *jump_table_sec = NULL;
ELF(Dyn) *dyn = 0, *dyn_end = 0;
const char *secstrings;
INT_BITS syms[NSYMS] = {};
@@ -78,6 +78,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
if (!strcmp(secstrings + GET_LE(&sh->sh_name),
".altinstructions"))
alt_sec = sh;
+ if (!strcmp(secstrings + GET_LE(&sh->sh_name),
+ "__jump_table"))
+ jump_table_sec = sh;
}
if (!symtab_hdr)
@@ -165,6 +168,12 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile, "\t.alt_len = %lu,\n",
(unsigned long)GET_LE(&alt_sec->sh_size));
}
+ if (jump_table_sec) {
+ fprintf(outfile, "\t.jump_table = %lu,\n",
+ (unsigned long)GET_LE(&jump_table_sec->sh_offset));
+ fprintf(outfile, "\t.jump_table_len = %lu,\n",
+ (unsigned long)GET_LE(&jump_table_sec->sh_size));
+ }
for (i = 0; i < NSYMS; i++) {
if (required_syms[i].export && syms[i])
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 0b8d9f6f0ce3..5c0e6491aefb 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -15,6 +15,7 @@
#include <linux/cpu.h>
#include <linux/ptrace.h>
#include <linux/time_namespace.h>
+#include <linux/jump_label.h>
#include <asm/pvclock.h>
#include <asm/vgtod.h>
#include <asm/proto.h>
@@ -38,6 +39,22 @@ static __init int vdso_setup(char *s)
__setup("vdso=", vdso_setup);
#endif
+#ifdef CONFIG_TIME_NS
+static __init int apply_jump_tables(struct vdso_jump_entry *ent, unsigned long nr)
+{
+ while (nr--) {
+ void *code_addr = (void *)ent + ent->code;
+ long target_addr = (long) ent->target - (ent->code + JUMP_LABEL_NOP_SIZE);
+ ((char *)code_addr)[0] = 0xe9; /* JMP rel32 */
+ *((long *)(code_addr + 1)) = (long)target_addr;
+
+ ent++;
+ }
+
+ return 0;
+}
+#endif
+
void __init init_vdso_image(struct vdso_image *image)
{
BUG_ON(image->size % PAGE_SIZE != 0);
@@ -51,6 +68,8 @@ void __init init_vdso_image(struct vdso_image *image)
return;
memcpy(image->text_timens, image->text, image->size);
+ apply_jump_tables((struct vdso_jump_entry *)(image->text_timens + image->jump_table),
+ image->jump_table_len / sizeof(struct vdso_jump_entry));
#endif
}
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 65191ce8e1cf..1784aa49cc82 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -51,6 +51,20 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool
return true;
}
+static __always_inline bool timens_static_branch_unlikely(void)
+{
+ asm_volatile_goto("1:\n\t"
+ ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t"
+ ".pushsection __jump_table, \"aw\"\n\t"
+ "2: .word 1b - 2b, %l[l_yes] - 2b\n\t"
+ ".popsection\n\t"
+ : : : : l_yes);
+
+ return false;
+l_yes:
+ return true;
+}
+
#else /* __ASSEMBLY__ */
.macro STATIC_JUMP_IF_TRUE target, key, def
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 583133446874..883151c3a032 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -16,6 +16,7 @@ struct vdso_image {
unsigned long size; /* Always a multiple of PAGE_SIZE */
unsigned long alt, alt_len;
+ unsigned long jump_table, jump_table_len;
long sym_vvar_start; /* Negative offset to the vvar area */
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 3e113a1fa0f1..69854a05d2f2 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -125,6 +125,11 @@ struct jump_entry {
long key; // key may be far away from the core kernel under KASLR
};
+struct vdso_jump_entry {
+ u16 code;
+ u16 target;
+};
+
static inline unsigned long jump_entry_code(const struct jump_entry *entry)
{
return (unsigned long)&entry->code + entry->code;
--
2.20.1
^ permalink raw reply related
* Re: [PATCH V31 25/25] debugfs: Disable open() when kernel is locked down
From: Greg KH @ 2019-03-27 18:29 UTC (permalink / raw)
To: Matthew Garrett
Cc: Andy Lutomirski, James Morris, LSM List, LKML, David Howells,
Linux API
In-Reply-To: <CACdnJus=zXgmNas+XVkWfe+ohAHEGMYLtfuYe0TH-eE=4p0oRw@mail.gmail.com>
On Wed, Mar 27, 2019 at 10:42:18AM -0700, Matthew Garrett wrote:
> On Wed, Mar 27, 2019 at 10:40 AM Andy Lutomirski <luto@kernel.org> wrote:
> > As far as I'm concerned, preventing root from crashing the system
> > should not be a design goal of lockdown at all. And I think that the
> > "integrity" mode should be as non-annoying as possible, so I think we
> > should allow reading from debugfs.
>
> I have no horse in this game - I'm happy to bring back the previous
> approach for integrity mode and block reads entirely in
> confidentiality mode, but I'd rather not spend another release cycle
> arguing about it.
I really do not care either way about any of this :)
greg k-h
^ permalink raw reply
* Re: [PATCH V31 25/25] debugfs: Disable open() when kernel is locked down
From: Greg KH @ 2019-03-27 18:31 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Matthew Garrett, James Morris, LSM List, LKML, David Howells,
Linux API, Matthew Garrett
In-Reply-To: <CALCETrUW=r=xyr4pGiGnn9zUEevf=z6PP3111Ds6qguV5SjYUQ@mail.gmail.com>
On Wed, Mar 27, 2019 at 10:39:53AM -0700, Andy Lutomirski wrote:
> On Tue, Mar 26, 2019 at 10:33 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Tue, Mar 26, 2019 at 10:29:41PM -0700, Andy Lutomirski wrote:
> > >
> > >
> > > > On Mar 26, 2019, at 10:06 PM, Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > >> On Tue, Mar 26, 2019 at 09:29:14PM -0700, Andy Lutomirski wrote:
> > > >>> On Tue, Mar 26, 2019 at 5:31 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >>>
> > > >>>> On Tue, Mar 26, 2019 at 12:20:24PM -0700, Andy Lutomirski wrote:
> > > >>>> On Tue, Mar 26, 2019 at 11:28 AM Matthew Garrett
> > > >>>> <matthewgarrett@google.com> wrote:
> > > >>>>>
> > > >>>>> From: Matthew Garrett <mjg59@google.com>
> > > >>>>>
> > > >>>>> debugfs has not been meaningfully audited in terms of ensuring that
> > > >>>>> userland cannot trample over the kernel. At Greg's request, disable
> > > >>>>> access to it entirely when the kernel is locked down. This is done at
> > > >>>>> open() time rather than init time as the kernel lockdown status may be
> > > >>>>> made stricter at runtime.
> > > >>>>
> > > >>>> Ugh. Some of those files are very useful. Could this perhaps still
> > > >>>> allow O_RDONLY if we're in INTEGRITY mode?
> > > >>>
> > > >>> Useful for what? Debugging, sure, but for "normal operation", no kernel
> > > >>> functionality should ever require debugfs. If it does, that's a bug and
> > > >>> should be fixed.
> > > >>>
> > > >>
> > > >> I semi-regularly read files in debugfs to diagnose things, and I think
> > > >> it would be good for this to work on distro kernels.
> > > >
> > > > Doing that for debugging is wonderful. People who want this type of
> > > > "lock down" are trading potential security for diagnositic ability.
> > > >
> > >
> > > I think you may be missing the point of splitting lockdown to separate integrity and confidentiality. Can you actually think of a case where *reading* a debugfs file can take over a kernel?
> >
> > Reading a debugfs file can expose loads of things that can help take
> > over a kernel, or at least make it easier. Pointer addresses, internal
> > system state, loads of other fun things. And before 4.14 or so, it was
> > pretty trivial to use it to oops the kernel as well (not an issue here
> > anymore, but people are right to be nervous).
> >
> > Personally, I think these are all just "confidentiality" type things,
> > but who really knows given the wild-west nature of debugfs (which is as
> > designed). And given that I think this patch series just crazy anyway,
> > I really don't care :)
> >
>
> As far as I'm concerned, preventing root from crashing the system
> should not be a design goal of lockdown at all. And I think that the
> "integrity" mode should be as non-annoying as possible, so I think we
> should allow reading from debugfs.
Sorry, the "crash the system" is not the issue here. The issue is if
everyone can "ensure" that "everything" in debugfs is "safe" for this
mode of "lock down". Given that no one has any idea of what really is
in debugfs, and to try to compare that with the design goals of what
"lock down" really is trying to achive, I think the goal of just giving
up and restricting access is fine if that makes people feel better about
this whole thing.
If this is locked down, it is going to cause distros more pain in
debugging user's issues, but that's their choice, not mine :)
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 4/4] tests: add pidfd_open() tests
From: Kees Cook @ 2019-03-27 19:36 UTC (permalink / raw)
To: Christian Brauner
Cc: Jann Horn, Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, LKML,
Arnd Bergmann, Alexey Dobriyan, Thomas Gleixner, Michael Kerrisk,
bl0pbl33p, Dmitry V. Levin, Andrew Morton, Oleg Nesterov,
nagarathnam.muthusamy, Aleksa Sarai, Al Viro, Joel Fernandes,
Daniel
In-Reply-To: <20190327162147.23198-5-christian@brauner.io>
On Wed, Mar 27, 2019 at 9:22 AM Christian Brauner <christian@brauner.io> wrote:
>
> This adds a simple test case for pidfd_open().
Thank you for the tests. :) It might be nice to see more extensive
negative testing -- making sure pids aren't reachable from mismatched
pid namespaces, etc, but this gets us basic regression testing. Also,
you might save some copy/paste code using kselftest_harness.h's
interfaces.
--
Kees Cook
^ permalink raw reply
* Re: [PATCH 2/4] pid: add pidfd_open()
From: Jonathan Kowalski @ 2019-03-27 19:38 UTC (permalink / raw)
To: Christian Brauner
Cc: Jann Horn, Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, linux-kernel,
Arnd Bergmann, Kees Cook, Alexey Dobriyan, Thomas Gleixner,
Michael Kerrisk-manpages, Dmitry V. Levin, Andrew Morton,
Oleg Nesterov, Nagarathnam Muthusamy, Aleksa Sarai, Al Viro
In-Reply-To: <20190327162147.23198-3-christian@brauner.io>
Christian,
Giving this some thought, it looks fine to me, but I am not convinced
this should take a pid argument. I much prefer if obtaining a pidfd
goes in the translation stuff, that has very real usecases, and you
would end up duplicating the same thing over in two places.
If the /proc/<PID> dir fd of a pidfd is relative to its procrootfd, a
pid's pidfd is also relative to its namespace. Currently, you change
things which would mean I now have to setns and spawn a child that
passes me back the pidfd for a pid in the said namespace.
I prefer Andy's version of
int procrootfd = open("/proc", O_DIRECTORY | O_RDONLY);
int procfd = pidfd_open(procrootfd, pidfd, PIDFD_TO_PROCFD); //name
this procfd_open, maybe
This is just a nicer race free openat.
But, Joel and you agreed that it makes sense to split out the
translation stuff out of pidfds.
My suggestion would be to extend ioctl_ns(2) then. It already provides
a (rather clumsy) mechanism to determine the relationship by comparing
st_dev and st_ino, which userspace can do itself for two namespace
descriptors. For translation, you can extend this namespace ioctl
(there is one to get a owner UID, you could add one to get a relative
PID).
Then, your pidfd call will be:
pidfd_open(pid_t pid, int ns, unsigned int flags);
You would also be able to compile out anything procfs related (include
the new API to procfs dir fd translation), otherwise, the way to open
pidfds is in this call, and without CONFIG_PROC_FS=Y, this is as good
as pidfd_open(pid_t pid) (of which a better version I propose above).
The new API should be its own thing, and the procfs translation tuple
its own thing, tied to the proc fs option. pidfds need not have any
relation to /proc.
For this procfd conversion system call, I would also lift any
namespace related restrictions if you are planning to, given the
constraint that I can only open a pidfd from an ancestor namespace
with the new pidfd_open system call, or acquire it through a
cooperative userspace process by fd passing otherwise, and I need the
/proc root fd, having both only permits me to open the said process's
/proc/<PID> dir fd subject to normal access restrictions. This means
the simplified procfd_open can be used to do metadata access without
even talking of PIDs at all, your process could live in its own world
and, given it has the authority to open the /proc directory, be able
to purely collect metadata based on the two file descriptors it is
given.
Once you have the restriction in the same call that allows you to open
a pidfd for an addressable PID from the given namespace fd, you can
finally remove the restriction to signal across namespaces once the
siginfo stuff is sorted out, as that will only work when you
explicitly push the fd into a sandbox, the process cannot get it out
of thin air on its own (and you already mentioned it has nothing to do
with security). What I do worry about is one can use NS_GET_PARENT
ioctl to get the parent pidns if the owning userns is the same, and
just passing that gives me back a pidfd for the task. **So, you might
want to add the constraint that the PID is actually reachable by the
current task as well, apart from being reachable in the passed in
namespace.**
Lastly, I also see no need of /proc/<PID> dir fd to pidfd conversion,
I would even recommend getting rid of that, so we only have one type
of pidfd, the anon inode one. What is the usecase behind that? It
would only be needed if you did not have a way to be able to metadata
access through a pidfd, which would be the case only prior to this
patch.
I think this would simplify a lot of things, and ioctl_ns(2) is
probably already the place to do comparison operations and query
operations on hierarichal namespaces, just adding the relative PID bit
will make it gain feature parity with translate_pid.
^ permalink raw reply
* Re: [PATCH 1/4] glibc: Perform rseq(2) registration at C startup and thread creation (v7)
From: Mathieu Desnoyers @ 2019-03-27 20:01 UTC (permalink / raw)
To: schwidefsky
Cc: Carlos O'Donell, Paul Burton, Will Deacon, Boqun Feng,
heiko carstens, gor, Russell King, ARM Linux,
Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, carlos,
Florian Weimer, Joseph Myers, Szabolcs Nagy, libc-alpha,
Thomas Gleixner, Ben Maurer, Peter Zijlstra, Paul E. McKenney,
Dave Watson <davejwatso>
In-Reply-To: <20190327101608.77b0de6f@mschwideX1>
----- On Mar 27, 2019, at 5:16 AM, schwidefsky schwidefsky@de.ibm.com wrote:
> On Mon, 25 Mar 2019 11:54:32 -0400 (EDT)
> Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote:
>
>> >> +++ b/sysdeps/unix/sysv/linux/s390/bits/rseq.h
>> [...]
>> >> +
>> >> +/* Signature required before each abort handler code. */
>> >> +#define RSEQ_SIG 0x53053053
>> >
>> > Why not a s390 specific value here?
>>
>> s390 also has the abort handler in a __rseq_failure section:
>>
>> #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
>> ".pushsection __rseq_failure, \"ax\"\n\t" \
>> ".long " __rseq_str(RSEQ_SIG) "\n\t" \
>> __rseq_str(label) ":\n\t" \
>> teardown \
>> "j %l[" __rseq_str(abort_label) "]\n\t" \
>> ".popsection\n\t"
>>
>> Same question applies as powerpc: since disassemblers will try to decode
>> that instruction, would it be better to define it as a valid one ?
>>
>> [...]
>
> A 4-byte sequence starting with 0x53 is decoded as a "diebr" instruction.
Based on the z/Architecture reference summary, it appears that the DIEBR
instruction's opcode is B353. So I suspect that just starting with 0x53 is
not sufficient to make it a valid opcode in the instruction set. Is it
something we should care about ?
> And please replace that "j %l[...]" with a "jg %l[...]", the branch target
> range of the "j" instruction is 64K, not enough for the general case.
I'll also need to use "jg" for RSEQ_ASM_DEFINE_CMPFAIL:
#define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
".pushsection __rseq_failure, \"ax\"\n\t" \
__rseq_str(label) ":\n\t" \
teardown \
"jg %l[" __rseq_str(cmpfail_label) "]\n\t" \
".popsection\n\t"
I'll prepare a fix for Linux selftests, and I already pushed a fix within
my librseq repository.
Thanks for pointing it out!
Mathieu
>
> --
> blue skies,
> Martin.
>
> "Reality continues to ruin my life." - Calvin.
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply
* Re: [PATCH 2/4] pid: add pidfd_open()
From: Jonathan Kowalski @ 2019-03-27 20:17 UTC (permalink / raw)
To: Christian Brauner
Cc: Jann Horn, Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, linux-kernel,
Arnd Bergmann, Kees Cook, Alexey Dobriyan, Thomas Gleixner,
Michael Kerrisk-manpages, Dmitry V. Levin, Andrew Morton,
Oleg Nesterov, Nagarathnam Muthusamy, Aleksa Sarai, Al Viro
In-Reply-To: <CAGLj2rG8xMMt8-+r-pauA_qi4aun2y_kBZMCZGkXWQsKJrb-Ww@mail.gmail.com>
On Wed, Mar 27, 2019 at 7:38 PM Jonathan Kowalski <bl0pbl33p@gmail.com> wrote:
> ...
> ... the process cannot get it out
> of thin air on its own (and you already mentioned it has nothing to do
> with security). What I do worry about is one can use NS_GET_PARENT
disregard this, it works as it should.
> ioctl to get the parent pidns if the owning userns is the same, and
> just passing that gives me back a pidfd for the task. **So, you might
> want to add the constraint that the PID is actually reachable by the
> current task as well, apart from being reachable in the passed in
> namespace.**
>
> Lastly, I also see no need of /proc/<PID> dir fd to pidfd conversion,
> I would even recommend getting rid of that, so we only have one type
> of pidfd, the anon inode one. What is the usecase behind that? It
> would only be needed if you did not have a way to be able to metadata
> access through a pidfd, which would be the case only prior to this
> patch.
>
> I think this would simplify a lot of things, and ioctl_ns(2) is
> probably already the place to do comparison operations and query
> operations on hierarichal namespaces, just adding the relative PID bit
> will make it gain feature parity with translate_pid.
^ permalink raw reply
* Re: [PATCH ghak90 V5 01/10] audit: collect audit task parameters
From: Ondrej Mosnacek @ 2019-03-27 20:33 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <76b039ef21de18f8b36af9c15f9d3fb7fd94ab21.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:33 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> The audit-related parameters in struct task_struct should ideally be
> collected together and accessed through a standard audit API.
>
> Collect the existing loginuid, sessionid and audit_context together in a
> new struct audit_task_info called "audit" in struct task_struct.
>
> Use kmem_cache to manage this pool of memory.
> Un-inline audit_free() to be able to always recover that memory.
>
> Please see the upstream github issue
> https://github.com/linux-audit/audit-kernel/issues/81
> but that issue has been closed with this patch included with
> https://github.com/linux-audit/audit-kernel/issues/90
>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> include/linux/audit.h | 49 +++++++++++++++++++++++------------
> include/linux/sched.h | 7 +----
> init/init_task.c | 3 +--
> init/main.c | 2 ++
> kernel/audit.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++--
> kernel/audit.h | 5 ++++
> kernel/auditsc.c | 26 ++++++++++---------
> kernel/fork.c | 1 -
> 8 files changed, 124 insertions(+), 40 deletions(-)
>
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 1e69d9fe16da..bde346e73f0c 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -86,6 +86,16 @@ struct audit_field {
> u32 op;
> };
>
> +struct audit_task_info {
> + kuid_t loginuid;
> + unsigned int sessionid;
> +#ifdef CONFIG_AUDITSYSCALL
> + struct audit_context *ctx;
> +#endif
> +};
> +
> +extern struct audit_task_info init_struct_audit;
> +
> extern int is_audit_feature_set(int which);
>
> extern int __init audit_register_class(int class, unsigned *list);
> @@ -122,6 +132,9 @@ struct audit_field {
> #ifdef CONFIG_AUDIT
> /* These are defined in audit.c */
> /* Public API */
> +extern int audit_alloc(struct task_struct *task);
> +extern void audit_free(struct task_struct *task);
> +extern void __init audit_task_init(void);
> extern __printf(4, 5)
> void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
> const char *fmt, ...);
> @@ -164,16 +177,28 @@ extern void audit_log_key(struct audit_buffer *ab,
>
> static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
> {
> - return tsk->loginuid;
> + if (!tsk->audit)
> + return INVALID_UID;
> + return tsk->audit->loginuid;
> }
>
> static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
> {
> - return tsk->sessionid;
> + if (!tsk->audit)
> + return AUDIT_SID_UNSET;
> + return tsk->audit->sessionid;
> }
>
> extern u32 audit_enabled;
> #else /* CONFIG_AUDIT */
> +static inline int audit_alloc(struct task_struct *task)
> +{
> + return 0;
> +}
> +static inline void audit_free(struct task_struct *task)
> +{ }
> +static inline void __init audit_task_init(void)
> +{ }
> static inline __printf(4, 5)
> void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
> const char *fmt, ...)
> @@ -239,8 +264,6 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
>
> /* These are defined in auditsc.c */
> /* Public API */
> -extern int audit_alloc(struct task_struct *task);
> -extern void __audit_free(struct task_struct *task);
> extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
> unsigned long a2, unsigned long a3);
> extern void __audit_syscall_exit(int ret_success, long ret_value);
> @@ -263,12 +286,14 @@ extern void audit_seccomp_actions_logged(const char *names,
>
> static inline void audit_set_context(struct task_struct *task, struct audit_context *ctx)
> {
> - task->audit_context = ctx;
> + task->audit->ctx = ctx;
> }
>
> static inline struct audit_context *audit_context(void)
> {
> - return current->audit_context;
> + if (!current->audit)
> + return NULL;
> + return current->audit->ctx;
> }
>
> static inline bool audit_dummy_context(void)
> @@ -276,11 +301,7 @@ static inline bool audit_dummy_context(void)
> void *p = audit_context();
> return !p || *(int *)p;
> }
> -static inline void audit_free(struct task_struct *task)
> -{
> - if (unlikely(task->audit_context))
> - __audit_free(task);
> -}
> +
> static inline void audit_syscall_entry(int major, unsigned long a0,
> unsigned long a1, unsigned long a2,
> unsigned long a3)
> @@ -470,12 +491,6 @@ static inline void audit_fanotify(unsigned int response)
> extern int audit_n_rules;
> extern int audit_signals;
> #else /* CONFIG_AUDITSYSCALL */
> -static inline int audit_alloc(struct task_struct *task)
> -{
> - return 0;
> -}
> -static inline void audit_free(struct task_struct *task)
> -{ }
> static inline void audit_syscall_entry(int major, unsigned long a0,
> unsigned long a1, unsigned long a2,
> unsigned long a3)
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 765119df759a..6850d1e48ace 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -31,7 +31,6 @@
> #include <linux/rseq.h>
>
> /* task_struct member predeclarations (sorted alphabetically): */
> -struct audit_context;
> struct backing_dev_info;
> struct bio_list;
> struct blk_plug;
> @@ -886,11 +885,7 @@ struct task_struct {
> struct callback_head *task_works;
>
> #ifdef CONFIG_AUDIT
> -#ifdef CONFIG_AUDITSYSCALL
> - struct audit_context *audit_context;
> -#endif
> - kuid_t loginuid;
> - unsigned int sessionid;
> + struct audit_task_info *audit;
> #endif
> struct seccomp seccomp;
>
> diff --git a/init/init_task.c b/init/init_task.c
> index 39c3109acc1a..f9685e1edda1 100644
> --- a/init/init_task.c
> +++ b/init/init_task.c
> @@ -122,8 +122,7 @@ struct task_struct init_task
> .thread_group = LIST_HEAD_INIT(init_task.thread_group),
> .thread_node = LIST_HEAD_INIT(init_signals.thread_head),
> #ifdef CONFIG_AUDIT
> - .loginuid = INVALID_UID,
> - .sessionid = AUDIT_SID_UNSET,
> + .audit = &init_struct_audit,
> #endif
> #ifdef CONFIG_PERF_EVENTS
> .perf_event_mutex = __MUTEX_INITIALIZER(init_task.perf_event_mutex),
> diff --git a/init/main.c b/init/main.c
> index e2e80ca3165a..8a1c36625d12 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -92,6 +92,7 @@
> #include <linux/rodata_test.h>
> #include <linux/jump_label.h>
> #include <linux/mem_encrypt.h>
> +#include <linux/audit.h>
>
> #include <asm/io.h>
> #include <asm/bugs.h>
> @@ -727,6 +728,7 @@ asmlinkage __visible void __init start_kernel(void)
> nsfs_init();
> cpuset_init();
> cgroup_init();
> + audit_task_init();
> taskstats_init_early();
> delayacct_init();
>
> diff --git a/kernel/audit.c b/kernel/audit.c
> index c89ea48c70a6..67498c5690bb 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -215,6 +215,73 @@ struct audit_reply {
> struct sk_buff *skb;
> };
>
> +static struct kmem_cache *audit_task_cache;
> +
> +void __init audit_task_init(void)
> +{
> + audit_task_cache = kmem_cache_create("audit_task",
> + sizeof(struct audit_task_info),
> + 0, SLAB_PANIC, NULL);
> +}
> +
> +/**
> + * audit_alloc - allocate an audit info block for a task
> + * @tsk: task
> + *
> + * Call audit_alloc_syscall to filter on the task information and
> + * allocate a per-task audit context if necessary. This is called from
> + * copy_process, so no lock is needed.
> + */
> +int audit_alloc(struct task_struct *tsk)
> +{
> + int ret = 0;
> + struct audit_task_info *info;
> +
> + info = kmem_cache_alloc(audit_task_cache, GFP_KERNEL);
> + if (!info) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + info->loginuid = audit_get_loginuid(current);
> + info->sessionid = audit_get_sessionid(current);
> + tsk->audit = info;
> +
> + ret = audit_alloc_syscall(tsk);
> + if (ret) {
> + tsk->audit = NULL;
> + kmem_cache_free(audit_task_cache, info);
> + }
> +out:
> + return ret;
> +}
> +
> +struct audit_task_info init_struct_audit = {
> + .loginuid = INVALID_UID,
> + .sessionid = AUDIT_SID_UNSET,
> +#ifdef CONFIG_AUDITSYSCALL
> + .ctx = NULL,
> +#endif
> +};
> +
> +/**
> + * audit_free - free per-task audit info
> + * @tsk: task whose audit info block to free
> + *
> + * Called from copy_process and do_exit
> + */
> +void audit_free(struct task_struct *tsk)
> +{
> + struct audit_task_info *info = tsk->audit;
> +
> + audit_free_syscall(tsk);
> + /* Freeing the audit_task_info struct must be performed after
> + * audit_log_exit() due to need for loginuid and sessionid.
> + */
> + info = tsk->audit;
> + tsk->audit = NULL;
> + kmem_cache_free(audit_task_cache, info);
> +}
> +
> /**
> * auditd_test_task - Check to see if a given task is an audit daemon
> * @task: the task to check
> @@ -2266,8 +2333,8 @@ int audit_set_loginuid(kuid_t loginuid)
> sessionid = (unsigned int)atomic_inc_return(&session_id);
> }
>
> - current->sessionid = sessionid;
> - current->loginuid = loginuid;
> + current->audit->sessionid = sessionid;
> + current->audit->loginuid = loginuid;
> out:
> audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc);
> return rc;
> diff --git a/kernel/audit.h b/kernel/audit.h
> index 958d5b8fc1b3..c00e2ee3c6b3 100644
> --- a/kernel/audit.h
> +++ b/kernel/audit.h
> @@ -264,6 +264,8 @@ extern void audit_log_d_path_exe(struct audit_buffer *ab,
> extern unsigned int audit_serial(void);
> extern int auditsc_get_stamp(struct audit_context *ctx,
> struct timespec64 *t, unsigned int *serial);
> +extern int audit_alloc_syscall(struct task_struct *tsk);
> +extern void audit_free_syscall(struct task_struct *tsk);
>
> extern void audit_put_watch(struct audit_watch *watch);
> extern void audit_get_watch(struct audit_watch *watch);
> @@ -305,6 +307,9 @@ extern void audit_filter_inodes(struct task_struct *tsk,
> extern struct list_head *audit_killed_trees(void);
> #else /* CONFIG_AUDITSYSCALL */
> #define auditsc_get_stamp(c, t, s) 0
> +#define audit_alloc_syscall(t) 0
> +#define audit_free_syscall(t) {}
> +
> #define audit_put_watch(w) {}
> #define audit_get_watch(w) {}
> #define audit_to_watch(k, p, l, o) (-EINVAL)
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index d1eab1d4a930..8090eff7868d 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -885,23 +885,25 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
> return context;
> }
>
> -/**
> - * audit_alloc - allocate an audit context block for a task
> +/*
> + * audit_alloc_syscall - allocate an audit context block for a task
> * @tsk: task
> *
> * Filter on the task information and allocate a per-task audit context
> * if necessary. Doing so turns on system call auditing for the
> - * specified task. This is called from copy_process, so no lock is
> - * needed.
> + * specified task. This is called from copy_process via audit_alloc, so
> + * no lock is needed.
> */
> -int audit_alloc(struct task_struct *tsk)
> +int audit_alloc_syscall(struct task_struct *tsk)
> {
> struct audit_context *context;
> enum audit_state state;
> char *key = NULL;
>
> - if (likely(!audit_ever_enabled))
> + if (likely(!audit_ever_enabled)) {
> + audit_set_context(tsk, NULL);
> return 0; /* Return if not auditing. */
> + }
>
> state = audit_filter_task(tsk, &key);
> if (state == AUDIT_DISABLED) {
> @@ -911,7 +913,7 @@ int audit_alloc(struct task_struct *tsk)
>
> if (!(context = audit_alloc_context(state))) {
> kfree(key);
> - audit_log_lost("out of memory in audit_alloc");
> + audit_log_lost("out of memory in audit_alloc_syscall");
> return -ENOMEM;
> }
> context->filterkey = key;
> @@ -1555,14 +1557,15 @@ static void audit_log_exit(void)
> }
>
> /**
> - * __audit_free - free a per-task audit context
> + * audit_free_syscall - free per-task audit context info
> * @tsk: task whose audit context block to free
> *
> - * Called from copy_process and do_exit
> + * Called from audit_free
> */
> -void __audit_free(struct task_struct *tsk)
> +void audit_free_syscall(struct task_struct *tsk)
> {
> - struct audit_context *context = tsk->audit_context;
> + struct audit_task_info *info = tsk->audit;
> + struct audit_context *context = info->ctx;
>
> if (!context)
> return;
> @@ -1585,7 +1588,6 @@ void __audit_free(struct task_struct *tsk)
> if (context->current_state == AUDIT_RECORD_CONTEXT)
> audit_log_exit();
> }
> -
> audit_set_context(tsk, NULL);
> audit_free_context(context);
> }
> diff --git a/kernel/fork.c b/kernel/fork.c
> index a60459947f18..1107bd8b8ad8 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -1836,7 +1836,6 @@ static __latent_entropy struct task_struct *copy_process(
> p->start_time = ktime_get_ns();
> p->real_start_time = ktime_get_boot_ns();
> p->io_context = NULL;
> - audit_set_context(p, NULL);
> cgroup_fork(p);
> #ifdef CONFIG_NUMA
> p->mempolicy = mpol_dup(p->mempolicy);
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH ghak90 V5 02/10] audit: add container id
From: Ondrej Mosnacek @ 2019-03-27 20:38 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: nhorman, linux-api, containers, LKML, David Howells,
Linux-Audit Mailing List, netfilter-devel, Eric W . Biederman,
Simo Sorce, netdev, linux-fsdevel, Eric Paris, Serge E. Hallyn
In-Reply-To: <19917968bb133ff644258b95dcbff522a83a2af1.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:33 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Implement the proc fs write to set the audit container identifier of a
> process, emitting an AUDIT_CONTAINER_OP record to document the event.
>
> This is a write from the container orchestrator task to a proc entry of
> the form /proc/PID/audit_containerid where PID is the process ID of the
> newly created task that is to become the first task in a container, or
> an additional task added to a container.
>
> The write expects up to a u64 value (unset: 18446744073709551615).
>
> The writer must have capability CAP_AUDIT_CONTROL.
>
> This will produce a record such as this:
> type=CONTAINER_OP msg=audit(2018-06-06 12:39:29.636:26949) : op=set opid=2209 old-contid=18446744073709551615 contid=123456 pid=628 auid=root uid=root tty=ttyS0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 comm=bash exe=/usr/bin/bash res=yes
>
> The "op" field indicates an initial set. The "pid" to "ses" fields are
> the orchestrator while the "opid" field is the object's PID, the process
> being "contained". Old and new audit container identifier values are
> given in the "contid" fields, while res indicates its success.
>
> It is not permitted to unset the audit container identifier.
> A child inherits its parent's audit container identifier.
>
> See: https://github.com/linux-audit/audit-kernel/issues/90
> See: https://github.com/linux-audit/audit-userspace/issues/51
> 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>
> Acked-by: Serge Hallyn <serge@hallyn.com>
> Acked-by: Steve Grubb <sgrubb@redhat.com>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Note that you have duplicate Signed-off here ^^
Took me a while to understand the flow in audit_set_contid(), but once
understood it all made perfect sense, so:
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/proc/base.c | 36 ++++++++++++++++++++++++
> include/linux/audit.h | 25 +++++++++++++++++
> include/uapi/linux/audit.h | 2 ++
> kernel/audit.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 131 insertions(+)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a23651ce6960..2505c46c8701 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -1294,6 +1294,40 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf,
> .read = proc_sessionid_read,
> .llseek = generic_file_llseek,
> };
> +
> +static ssize_t proc_contid_write(struct file *file, const char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct inode *inode = file_inode(file);
> + u64 contid;
> + int rv;
> + struct task_struct *task = get_proc_task(inode);
> +
> + if (!task)
> + return -ESRCH;
> + if (*ppos != 0) {
> + /* No partial writes. */
> + put_task_struct(task);
> + return -EINVAL;
> + }
> +
> + rv = kstrtou64_from_user(buf, count, 10, &contid);
> + if (rv < 0) {
> + put_task_struct(task);
> + return rv;
> + }
> +
> + rv = audit_set_contid(task, contid);
> + put_task_struct(task);
> + if (rv < 0)
> + return rv;
> + return count;
> +}
> +
> +static const struct file_operations proc_contid_operations = {
> + .write = proc_contid_write,
> + .llseek = generic_file_llseek,
> +};
> #endif
>
> #ifdef CONFIG_FAULT_INJECTION
> @@ -3005,6 +3039,7 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
> #ifdef CONFIG_AUDIT
> REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> REG("sessionid", S_IRUGO, proc_sessionid_operations),
> + REG("audit_containerid", S_IWUSR, proc_contid_operations),
> #endif
> #ifdef CONFIG_FAULT_INJECTION
> REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> @@ -3393,6 +3428,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
> #ifdef CONFIG_AUDIT
> REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> REG("sessionid", S_IRUGO, proc_sessionid_operations),
> + REG("audit_containerid", S_IWUSR, proc_contid_operations),
> #endif
> #ifdef CONFIG_FAULT_INJECTION
> REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index bde346e73f0c..301337776193 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -89,6 +89,7 @@ struct audit_field {
> struct audit_task_info {
> kuid_t loginuid;
> unsigned int sessionid;
> + u64 contid;
> #ifdef CONFIG_AUDITSYSCALL
> struct audit_context *ctx;
> #endif
> @@ -189,6 +190,15 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
> return tsk->audit->sessionid;
> }
>
> +extern int audit_set_contid(struct task_struct *tsk, u64 contid);
> +
> +static inline u64 audit_get_contid(struct task_struct *tsk)
> +{
> + if (!tsk->audit)
> + return AUDIT_CID_UNSET;
> + return tsk->audit->contid;
> +}
> +
> extern u32 audit_enabled;
> #else /* CONFIG_AUDIT */
> static inline int audit_alloc(struct task_struct *task)
> @@ -250,6 +260,11 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
> return AUDIT_SID_UNSET;
> }
>
> +static inline u64 audit_get_contid(struct task_struct *tsk)
> +{
> + return AUDIT_CID_UNSET;
> +}
> +
> #define audit_enabled AUDIT_OFF
> #endif /* CONFIG_AUDIT */
>
> @@ -606,6 +621,16 @@ static inline bool audit_loginuid_set(struct task_struct *tsk)
> return uid_valid(audit_get_loginuid(tsk));
> }
>
> +static inline bool audit_contid_valid(u64 contid)
> +{
> + return contid != AUDIT_CID_UNSET;
> +}
> +
> +static inline bool audit_contid_set(struct task_struct *tsk)
> +{
> + return audit_contid_valid(audit_get_contid(tsk));
> +}
> +
> static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
> {
> audit_log_n_string(ab, buf, strlen(buf));
> diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> index 36a7e3f18e69..d475cf3b4d7f 100644
> --- a/include/uapi/linux/audit.h
> +++ b/include/uapi/linux/audit.h
> @@ -71,6 +71,7 @@
> #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */
> #define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */
> #define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */
> +#define AUDIT_CONTAINER_OP 1020 /* Define the container id and information */
>
> #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */
> #define AUDIT_USER_AVC 1107 /* We filter this differently */
> @@ -471,6 +472,7 @@ struct audit_tty_status {
>
> #define AUDIT_UID_UNSET (unsigned int)-1
> #define AUDIT_SID_UNSET ((unsigned int)-1)
> +#define AUDIT_CID_UNSET ((u64)-1)
>
> /* audit_rule_data supports filter rules with both integer and string
> * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
> diff --git a/kernel/audit.c b/kernel/audit.c
> index 67498c5690bb..b5c702abeb42 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -244,6 +244,7 @@ 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);
> tsk->audit = info;
>
> ret = audit_alloc_syscall(tsk);
> @@ -258,6 +259,7 @@ int audit_alloc(struct task_struct *tsk)
> struct audit_task_info init_struct_audit = {
> .loginuid = INVALID_UID,
> .sessionid = AUDIT_SID_UNSET,
> + .contid = AUDIT_CID_UNSET,
> #ifdef CONFIG_AUDITSYSCALL
> .ctx = NULL,
> #endif
> @@ -2341,6 +2343,72 @@ int audit_set_loginuid(kuid_t loginuid)
> }
>
> /**
> + * audit_set_contid - set current task's audit contid
> + * @contid: contid value
> + *
> + * Returns 0 on success, -EPERM on permission failure.
> + *
> + * Called (set) from fs/proc/base.c::proc_contid_write().
> + */
> +int audit_set_contid(struct task_struct *task, u64 contid)
> +{
> + u64 oldcontid;
> + int rc = 0;
> + struct audit_buffer *ab;
> + uid_t uid;
> + struct tty_struct *tty;
> + char comm[sizeof(current->comm)];
> +
> + task_lock(task);
> + /* Can't set if audit disabled */
> + if (!task->audit) {
> + task_unlock(task);
> + return -ENOPROTOOPT;
> + }
> + oldcontid = audit_get_contid(task);
> + read_lock(&tasklist_lock);
> + /* Don't allow the audit containerid to be unset */
> + if (!audit_contid_valid(contid))
> + rc = -EINVAL;
> + /* if we don't have caps, reject */
> + else if (!capable(CAP_AUDIT_CONTROL))
> + rc = -EPERM;
> + /* if task has children or is not single-threaded, deny */
> + else if (!list_empty(&task->children))
> + rc = -EBUSY;
> + else if (!(thread_group_leader(task) && thread_group_empty(task)))
> + rc = -EALREADY;
> + read_unlock(&tasklist_lock);
> + if (!rc)
> + task->audit->contid = contid;
> + task_unlock(task);
> +
> + if (!audit_enabled)
> + return rc;
> +
> + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP);
> + if (!ab)
> + return rc;
> +
> + uid = from_kuid(&init_user_ns, task_uid(current));
> + tty = audit_get_tty();
> + audit_log_format(ab, "op=set opid=%d old-contid=%llu contid=%llu pid=%d uid=%u auid=%u tty=%s ses=%u",
> + task_tgid_nr(task), oldcontid, contid,
> + task_tgid_nr(current), uid,
> + from_kuid(&init_user_ns, audit_get_loginuid(current)),
> + tty ? tty_name(tty) : "(none)",
> + audit_get_sessionid(current));
> + audit_put_tty(tty);
> + audit_log_task_context(ab);
> + audit_log_format(ab, " comm=");
> + audit_log_untrustedstring(ab, get_task_comm(comm, current));
> + audit_log_d_path_exe(ab, current->mm);
> + audit_log_format(ab, " res=%d", !rc);
> + audit_log_end(ab);
> + return rc;
> +}
> +
> +/**
> * audit_log_end - end one audit record
> * @ab: the audit_buffer
> *
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH 1/4] glibc: Perform rseq(2) registration at C startup and thread creation (v7)
From: Carlos O'Donell @ 2019-03-27 20:38 UTC (permalink / raw)
To: Martin Schwidefsky, Mathieu Desnoyers
Cc: Paul Burton, Will Deacon, Boqun Feng, Heiko Carstens,
Vasily Gorbik, Russell King, Benjamin Herrenschmidt,
Paul Mackerras, Michael Ellerman, carlos, Florian Weimer,
Joseph Myers, Szabolcs Nagy, libc-alpha, Thomas Gleixner,
Ben Maurer, Peter Zijlstra, Paul E. McKenney, Dave Watson,
Paul Turner
In-Reply-To: <20190327101608.77b0de6f@mschwideX1>
On 3/27/19 5:16 AM, Martin Schwidefsky wrote:
> On Mon, 25 Mar 2019 11:54:32 -0400 (EDT)
> Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote:
>
>>>> +++ b/sysdeps/unix/sysv/linux/s390/bits/rseq.h
>> [...]
>>>> +
>>>> +/* Signature required before each abort handler code. */
>>>> +#define RSEQ_SIG 0x53053053
>>>
>>> Why not a s390 specific value here?
>>
>> s390 also has the abort handler in a __rseq_failure section:
>>
>> #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
>> ".pushsection __rseq_failure, \"ax\"\n\t" \
>> ".long " __rseq_str(RSEQ_SIG) "\n\t" \
>> __rseq_str(label) ":\n\t" \
>> teardown \
>> "j %l[" __rseq_str(abort_label) "]\n\t" \
>> ".popsection\n\t"
>>
>> Same question applies as powerpc: since disassemblers will try to decode
>> that instruction, would it be better to define it as a valid one ?
>>
>> [...]
>
> A 4-byte sequence starting with 0x53 is decoded as a "diebr" instruction.
> And please replace that "j %l[...]" with a "jg %l[...]", the branch target
> range of the "j" instruction is 64K, not enough for the general case.
Why was this particular operated selected?
So on s390 the RSEQ_SIG will show up as an unexpected "divide to integer"
instruction that can't be reached by any control flow?
Can we use a NOP with a unique value in an immediate operand?
The goal being to have something that won't confuse during a debug
session, or that the debugger can ignore (like constant pools on Arm)
--
Cheers,
Carlos.
^ permalink raw reply
* Re: [PATCH ghak90 V5 02/10] audit: add container id
From: Richard Guy Briggs @ 2019-03-27 20:44 UTC (permalink / raw)
To: Ondrej Mosnacek
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <CAFqZXNtyzb8LVghoKEwYDC2vkZVbytcEahwpP49_QDR+tM=PNQ@mail.gmail.com>
On 2019-03-27 21:38, Ondrej Mosnacek wrote:
> On Fri, Mar 15, 2019 at 7:33 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> > Implement the proc fs write to set the audit container identifier of a
> > process, emitting an AUDIT_CONTAINER_OP record to document the event.
> >
> > This is a write from the container orchestrator task to a proc entry of
> > the form /proc/PID/audit_containerid where PID is the process ID of the
> > newly created task that is to become the first task in a container, or
> > an additional task added to a container.
> >
> > The write expects up to a u64 value (unset: 18446744073709551615).
> >
> > The writer must have capability CAP_AUDIT_CONTROL.
> >
> > This will produce a record such as this:
> > type=CONTAINER_OP msg=audit(2018-06-06 12:39:29.636:26949) : op=set opid=2209 old-contid=18446744073709551615 contid=123456 pid=628 auid=root uid=root tty=ttyS0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 comm=bash exe=/usr/bin/bash res=yes
> >
> > The "op" field indicates an initial set. The "pid" to "ses" fields are
> > the orchestrator while the "opid" field is the object's PID, the process
> > being "contained". Old and new audit container identifier values are
> > given in the "contid" fields, while res indicates its success.
> >
> > It is not permitted to unset the audit container identifier.
> > A child inherits its parent's audit container identifier.
> >
> > See: https://github.com/linux-audit/audit-kernel/issues/90
> > See: https://github.com/linux-audit/audit-userspace/issues/51
> > 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>
> > Acked-by: Serge Hallyn <serge@hallyn.com>
> > Acked-by: Steve Grubb <sgrubb@redhat.com>
> > Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
>
> Note that you have duplicate Signed-off here ^^
Yeah, I caught a few of those for v6 and a number of other formatting
issues.
> Took me a while to understand the flow in audit_set_contid(), but once
> understood it all made perfect sense, so:
>
> Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
>
>
> > ---
> > fs/proc/base.c | 36 ++++++++++++++++++++++++
> > include/linux/audit.h | 25 +++++++++++++++++
> > include/uapi/linux/audit.h | 2 ++
> > kernel/audit.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 131 insertions(+)
> >
> > diff --git a/fs/proc/base.c b/fs/proc/base.c
> > index a23651ce6960..2505c46c8701 100644
> > --- a/fs/proc/base.c
> > +++ b/fs/proc/base.c
> > @@ -1294,6 +1294,40 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf,
> > .read = proc_sessionid_read,
> > .llseek = generic_file_llseek,
> > };
> > +
> > +static ssize_t proc_contid_write(struct file *file, const char __user *buf,
> > + size_t count, loff_t *ppos)
> > +{
> > + struct inode *inode = file_inode(file);
> > + u64 contid;
> > + int rv;
> > + struct task_struct *task = get_proc_task(inode);
> > +
> > + if (!task)
> > + return -ESRCH;
> > + if (*ppos != 0) {
> > + /* No partial writes. */
> > + put_task_struct(task);
> > + return -EINVAL;
> > + }
> > +
> > + rv = kstrtou64_from_user(buf, count, 10, &contid);
> > + if (rv < 0) {
> > + put_task_struct(task);
> > + return rv;
> > + }
> > +
> > + rv = audit_set_contid(task, contid);
> > + put_task_struct(task);
> > + if (rv < 0)
> > + return rv;
> > + return count;
> > +}
> > +
> > +static const struct file_operations proc_contid_operations = {
> > + .write = proc_contid_write,
> > + .llseek = generic_file_llseek,
> > +};
> > #endif
> >
> > #ifdef CONFIG_FAULT_INJECTION
> > @@ -3005,6 +3039,7 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
> > #ifdef CONFIG_AUDIT
> > REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> > REG("sessionid", S_IRUGO, proc_sessionid_operations),
> > + REG("audit_containerid", S_IWUSR, proc_contid_operations),
> > #endif
> > #ifdef CONFIG_FAULT_INJECTION
> > REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> > @@ -3393,6 +3428,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
> > #ifdef CONFIG_AUDIT
> > REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> > REG("sessionid", S_IRUGO, proc_sessionid_operations),
> > + REG("audit_containerid", S_IWUSR, proc_contid_operations),
> > #endif
> > #ifdef CONFIG_FAULT_INJECTION
> > REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> > diff --git a/include/linux/audit.h b/include/linux/audit.h
> > index bde346e73f0c..301337776193 100644
> > --- a/include/linux/audit.h
> > +++ b/include/linux/audit.h
> > @@ -89,6 +89,7 @@ struct audit_field {
> > struct audit_task_info {
> > kuid_t loginuid;
> > unsigned int sessionid;
> > + u64 contid;
> > #ifdef CONFIG_AUDITSYSCALL
> > struct audit_context *ctx;
> > #endif
> > @@ -189,6 +190,15 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
> > return tsk->audit->sessionid;
> > }
> >
> > +extern int audit_set_contid(struct task_struct *tsk, u64 contid);
> > +
> > +static inline u64 audit_get_contid(struct task_struct *tsk)
> > +{
> > + if (!tsk->audit)
> > + return AUDIT_CID_UNSET;
> > + return tsk->audit->contid;
> > +}
> > +
> > extern u32 audit_enabled;
> > #else /* CONFIG_AUDIT */
> > static inline int audit_alloc(struct task_struct *task)
> > @@ -250,6 +260,11 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
> > return AUDIT_SID_UNSET;
> > }
> >
> > +static inline u64 audit_get_contid(struct task_struct *tsk)
> > +{
> > + return AUDIT_CID_UNSET;
> > +}
> > +
> > #define audit_enabled AUDIT_OFF
> > #endif /* CONFIG_AUDIT */
> >
> > @@ -606,6 +621,16 @@ static inline bool audit_loginuid_set(struct task_struct *tsk)
> > return uid_valid(audit_get_loginuid(tsk));
> > }
> >
> > +static inline bool audit_contid_valid(u64 contid)
> > +{
> > + return contid != AUDIT_CID_UNSET;
> > +}
> > +
> > +static inline bool audit_contid_set(struct task_struct *tsk)
> > +{
> > + return audit_contid_valid(audit_get_contid(tsk));
> > +}
> > +
> > static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
> > {
> > audit_log_n_string(ab, buf, strlen(buf));
> > diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> > index 36a7e3f18e69..d475cf3b4d7f 100644
> > --- a/include/uapi/linux/audit.h
> > +++ b/include/uapi/linux/audit.h
> > @@ -71,6 +71,7 @@
> > #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */
> > #define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */
> > #define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */
> > +#define AUDIT_CONTAINER_OP 1020 /* Define the container id and information */
> >
> > #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */
> > #define AUDIT_USER_AVC 1107 /* We filter this differently */
> > @@ -471,6 +472,7 @@ struct audit_tty_status {
> >
> > #define AUDIT_UID_UNSET (unsigned int)-1
> > #define AUDIT_SID_UNSET ((unsigned int)-1)
> > +#define AUDIT_CID_UNSET ((u64)-1)
> >
> > /* audit_rule_data supports filter rules with both integer and string
> > * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
> > diff --git a/kernel/audit.c b/kernel/audit.c
> > index 67498c5690bb..b5c702abeb42 100644
> > --- a/kernel/audit.c
> > +++ b/kernel/audit.c
> > @@ -244,6 +244,7 @@ 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);
> > tsk->audit = info;
> >
> > ret = audit_alloc_syscall(tsk);
> > @@ -258,6 +259,7 @@ int audit_alloc(struct task_struct *tsk)
> > struct audit_task_info init_struct_audit = {
> > .loginuid = INVALID_UID,
> > .sessionid = AUDIT_SID_UNSET,
> > + .contid = AUDIT_CID_UNSET,
> > #ifdef CONFIG_AUDITSYSCALL
> > .ctx = NULL,
> > #endif
> > @@ -2341,6 +2343,72 @@ int audit_set_loginuid(kuid_t loginuid)
> > }
> >
> > /**
> > + * audit_set_contid - set current task's audit contid
> > + * @contid: contid value
> > + *
> > + * Returns 0 on success, -EPERM on permission failure.
> > + *
> > + * Called (set) from fs/proc/base.c::proc_contid_write().
> > + */
> > +int audit_set_contid(struct task_struct *task, u64 contid)
> > +{
> > + u64 oldcontid;
> > + int rc = 0;
> > + struct audit_buffer *ab;
> > + uid_t uid;
> > + struct tty_struct *tty;
> > + char comm[sizeof(current->comm)];
> > +
> > + task_lock(task);
> > + /* Can't set if audit disabled */
> > + if (!task->audit) {
> > + task_unlock(task);
> > + return -ENOPROTOOPT;
> > + }
> > + oldcontid = audit_get_contid(task);
> > + read_lock(&tasklist_lock);
> > + /* Don't allow the audit containerid to be unset */
> > + if (!audit_contid_valid(contid))
> > + rc = -EINVAL;
> > + /* if we don't have caps, reject */
> > + else if (!capable(CAP_AUDIT_CONTROL))
> > + rc = -EPERM;
> > + /* if task has children or is not single-threaded, deny */
> > + else if (!list_empty(&task->children))
> > + rc = -EBUSY;
> > + else if (!(thread_group_leader(task) && thread_group_empty(task)))
> > + rc = -EALREADY;
> > + read_unlock(&tasklist_lock);
> > + if (!rc)
> > + task->audit->contid = contid;
> > + task_unlock(task);
> > +
> > + if (!audit_enabled)
> > + return rc;
> > +
> > + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP);
> > + if (!ab)
> > + return rc;
> > +
> > + uid = from_kuid(&init_user_ns, task_uid(current));
> > + tty = audit_get_tty();
> > + audit_log_format(ab, "op=set opid=%d old-contid=%llu contid=%llu pid=%d uid=%u auid=%u tty=%s ses=%u",
> > + task_tgid_nr(task), oldcontid, contid,
> > + task_tgid_nr(current), uid,
> > + from_kuid(&init_user_ns, audit_get_loginuid(current)),
> > + tty ? tty_name(tty) : "(none)",
> > + audit_get_sessionid(current));
> > + audit_put_tty(tty);
> > + audit_log_task_context(ab);
> > + audit_log_format(ab, " comm=");
> > + audit_log_untrustedstring(ab, get_task_comm(comm, current));
> > + audit_log_d_path_exe(ab, current->mm);
> > + audit_log_format(ab, " res=%d", !rc);
> > + audit_log_end(ab);
> > + return rc;
> > +}
> > +
> > +/**
> > * audit_log_end - end one audit record
> > * @ab: the audit_buffer
> > *
> > --
> > 1.8.3.1
> >
>
> --
> Ondrej Mosnacek <omosnace at redhat dot com>
> Software Engineer, Security Technologies
> Red Hat, Inc.
- RGB
--
Richard Guy Briggs <rgb@redhat.com>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
IRC: rgb, SunRaycer
Voice: +1.647.777.2635, Internal: (81) 32635
^ permalink raw reply
* Re: [PATCH ghak90 V5 03/10] audit: read container ID of a process
From: Ondrej Mosnacek @ 2019-03-27 20:44 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <25543e3db4d8f3853119f92c09a60664555249a2.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:33 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Add support for reading the audit container identifier from the proc
> filesystem.
>
> This is a read from the proc entry of the form
> /proc/PID/audit_containerid where PID is the process ID of the task
> whose audit container identifier is sought.
>
> The read expects up to a u64 value (unset: 18446744073709551615).
>
> This read requires CAP_AUDIT_CONTROL.
>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> Acked-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/proc/base.c | 23 +++++++++++++++++++++--
> 1 file changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 2505c46c8701..0b833cbdf5b6 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -1295,6 +1295,24 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf,
> .llseek = generic_file_llseek,
> };
>
> +static ssize_t proc_contid_read(struct file *file, char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct inode *inode = file_inode(file);
> + struct task_struct *task = get_proc_task(inode);
> + ssize_t length;
> + char tmpbuf[TMPBUFLEN*2];
> +
> + if (!task)
> + return -ESRCH;
> + /* if we don't have caps, reject */
> + if (!capable(CAP_AUDIT_CONTROL))
> + return -EPERM;
> + length = scnprintf(tmpbuf, TMPBUFLEN*2, "%llu", audit_get_contid(task));
> + put_task_struct(task);
> + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
> +}
> +
> static ssize_t proc_contid_write(struct file *file, const char __user *buf,
> size_t count, loff_t *ppos)
> {
> @@ -1325,6 +1343,7 @@ static ssize_t proc_contid_write(struct file *file, const char __user *buf,
> }
>
> static const struct file_operations proc_contid_operations = {
> + .read = proc_contid_read,
> .write = proc_contid_write,
> .llseek = generic_file_llseek,
> };
> @@ -3039,7 +3058,7 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
> #ifdef CONFIG_AUDIT
> REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> REG("sessionid", S_IRUGO, proc_sessionid_operations),
> - REG("audit_containerid", S_IWUSR, proc_contid_operations),
> + REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations),
> #endif
> #ifdef CONFIG_FAULT_INJECTION
> REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> @@ -3428,7 +3447,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
> #ifdef CONFIG_AUDIT
> REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
> REG("sessionid", S_IRUGO, proc_sessionid_operations),
> - REG("audit_containerid", S_IWUSR, proc_contid_operations),
> + REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations),
> #endif
> #ifdef CONFIG_FAULT_INJECTION
> REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH 4/4] tests: add pidfd_open() tests
From: Christian Brauner @ 2019-03-27 20:55 UTC (permalink / raw)
To: Kees Cook
Cc: Jann Horn, Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, LKML,
Arnd Bergmann, Alexey Dobriyan, Thomas Gleixner, Michael Kerrisk,
bl0pbl33p, Dmitry V. Levin, Andrew Morton, Oleg Nesterov,
nagarathnam.muthusamy, Aleksa Sarai, Al Viro, Joel Fernandes,
Daniel
In-Reply-To: <CAGXu5jL-3XbFzT-1qzEiXE+DTPi_xi7dEtyAMTwBJU0BqrFeKQ@mail.gmail.com>
On Wed, Mar 27, 2019 at 12:36:46PM -0700, Kees Cook wrote:
> On Wed, Mar 27, 2019 at 9:22 AM Christian Brauner <christian@brauner.io> wrote:
> >
> > This adds a simple test case for pidfd_open().
>
> Thank you for the tests. :) It might be nice to see more extensive
> negative testing -- making sure pids aren't reachable from mismatched
There'll be more tests for sure! Once people say that they are happy
with the design I can put in the effort to expand the tests to more
elaborate scenarios. :)
> pid namespaces, etc, but this gets us basic regression testing. Also,
> you might save some copy/paste code using kselftest_harness.h's
> interfaces.
>
> --
> Kees Cook
^ permalink raw reply
* Re: [PATCH 2/4] pid: add pidfd_open()
From: Christian Brauner @ 2019-03-27 20:59 UTC (permalink / raw)
To: Yann Droneaud
Cc: jannh, khlebnikov, luto, dhowells, serge, ebiederm, linux-api,
linux-kernel, arnd, keescook, adobriyan, tglx, mtk.manpages,
bl0pbl33p, ldv, akpm, oleg, nagarathnam.muthusamy, cyphar, viro,
joel, dancol
In-Reply-To: <9254286c02dbe883c14e38ed2af0022d36b17355.camel@opteya.com>
On Wed, Mar 27, 2019 at 06:21:24PM +0100, Yann Droneaud wrote:
> Le mercredi 27 mars 2019 à 17:21 +0100, Christian Brauner a écrit :
>
> > diff --git a/kernel/pid.c b/kernel/pid.c
> > index 20881598bdfa..c9e24e726aba 100644
> > --- a/kernel/pid.c
> > +++ b/kernel/pid.c
> > @@ -26,8 +26,10 @@
> > +SYSCALL_DEFINE4(pidfd_open, pid_t, pid, int, procfd, int, pidfd,
> > unsigned int,
> > + flags)
> > +{
> > + long fd = -EINVAL;
> > +
> > + if (flags & ~(PIDFD_TO_PROCFD | PROCFD_TO_PIDFD))
> > + return -EINVAL;
> > +
> > + if (!flags) {
> > + struct pid *pidfd_pid;
> > +
> > + if (pid <= 0)
> > + return -EINVAL;
> > +
> > + if (procfd != -1 || pidfd != -1)
> > + return -EINVAL;
> > +
> > + rcu_read_lock();
> > + pidfd_pid = get_pid(find_pid_ns(pid, task_active_pid_ns(current)));
> > + rcu_read_unlock();
> > +
> > + fd = pidfd_create_fd(pidfd_pid, O_CLOEXEC);
> > + put_pid(pidfd_pid);
> > + } else if (flags & PIDFD_TO_PROCFD) {
>
> [...]
>
> > + } else if (flags & PROCFD_TO_PIDFD) {
> > + if (flags & ~PROCFD_TO_PIDFD)
> > + return -EINVAL;
> > +
> > + if (pid != -1)
> > + return -EINVAL;
> > +
> > + if (pidfd >= 0)
> >
>
> I think it can be stricter with:
>
> if (pidfd != -1)
Yes.
>
> (and match the check done for flag == 0).
>
> Regards.
>
> --
> Yann Droneaud
> OPTEYA
>
>
^ permalink raw reply
* Re: [PATCH ghak90 V5 04/10] audit: log container info of syscalls
From: Ondrej Mosnacek @ 2019-03-27 21:01 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <85fcd0a81adef25cb60b2e479bbb380e76dbf999.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:34 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Create a new audit record AUDIT_CONTAINER_ID to document the audit
> container identifier of a process if it is present.
>
> Called from audit_log_exit(), syscalls are covered.
>
> A sample raw event:
> type=SYSCALL msg=audit(1519924845.499:257): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=56374e1cef30 a2=241 a3=1b6 items=2 ppid=606 pid=635 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="tmpcontainerid"
> type=CWD msg=audit(1519924845.499:257): cwd="/root"
> type=PATH msg=audit(1519924845.499:257): item=0 name="/tmp/" inode=13863 dev=00:27 mode=041777 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:tmp_t:s0 nametype= PARENT cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0
> type=PATH msg=audit(1519924845.499:257): item=1 name="/tmp/tmpcontainerid" inode=17729 dev=00:27 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 nametype=CREATE cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0
> type=PROCTITLE msg=audit(1519924845.499:257): proctitle=62617368002D6300736C65657020313B206563686F2074657374203E202F746D702F746D70636F6E7461696E65726964
> type=CONTAINER_ID msg=audit(1519924845.499:257): contid=123458
>
> See: https://github.com/linux-audit/audit-kernel/issues/90
> See: https://github.com/linux-audit/audit-userspace/issues/51
> 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>
> Acked-by: Serge Hallyn <serge@hallyn.com>
> Acked-by: Steve Grubb <sgrubb@redhat.com>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Barring one minor nit below,
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> include/linux/audit.h | 5 +++++
> include/uapi/linux/audit.h | 1 +
> kernel/audit.c | 21 +++++++++++++++++++++
> kernel/auditsc.c | 2 ++
> 4 files changed, 29 insertions(+)
>
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 301337776193..43438192ca2a 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -199,6 +199,8 @@ static inline u64 audit_get_contid(struct task_struct *tsk)
> return tsk->audit->contid;
> }
>
> +extern void audit_log_contid(struct audit_context *context, u64 contid);
> +
> extern u32 audit_enabled;
> #else /* CONFIG_AUDIT */
> static inline int audit_alloc(struct task_struct *task)
> @@ -265,6 +267,9 @@ static inline u64 audit_get_contid(struct task_struct *tsk)
> return AUDIT_CID_UNSET;
> }
>
> +static inline void audit_log_contid(struct audit_context *context, u64 contid)
> +{ }
> +
> #define audit_enabled AUDIT_OFF
> #endif /* CONFIG_AUDIT */
>
> diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> index d475cf3b4d7f..a6383e28b2c8 100644
> --- a/include/uapi/linux/audit.h
> +++ b/include/uapi/linux/audit.h
> @@ -115,6 +115,7 @@
> #define AUDIT_REPLACE 1329 /* Replace auditd if this packet unanswerd */
> #define AUDIT_KERN_MODULE 1330 /* Kernel Module events */
> #define AUDIT_FANOTIFY 1331 /* Fanotify access decision */
> +#define AUDIT_CONTAINER_ID 1332 /* Container ID */
>
> #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
> #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
> diff --git a/kernel/audit.c b/kernel/audit.c
> index b5c702abeb42..8cc0e88d7f2a 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -2127,6 +2127,27 @@ void audit_log_session_info(struct audit_buffer *ab)
> audit_log_format(ab, "auid=%u ses=%u", auid, sessionid);
> }
>
> +/*
> + * audit_log_contid - report container info
> + * @context: task or local context for record
> + * @contid: container ID to report
> + */
> +void audit_log_contid(struct audit_context *context, u64 contid)
> +{
> + struct audit_buffer *ab;
> +
> + if (!audit_contid_valid(contid))
> + return;
> + /* Generate AUDIT_CONTAINER_ID record with container ID */
> + ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONTAINER_ID);
> + if (!ab)
> + return;
> + audit_log_format(ab, "contid=%llu", contid);
Just realized that you *might* get a compiler/static checker warning
since u64 could technically be something else than unsigned long long
on some arches... maybe this is not case in the kernel, but might be
safer to cast it to unsigned long long before passing to
audit_log_format(). Possibly there are similar occurrences in previous
(later) patches that I didn't (won't) notice.
> + audit_log_end(ab);
> + return;
> +}
> +EXPORT_SYMBOL(audit_log_contid);
> +
> void audit_log_key(struct audit_buffer *ab, char *key)
> {
> audit_log_format(ab, " key=");
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index 8090eff7868d..a8c8b44b954d 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -1548,6 +1548,8 @@ static void audit_log_exit(void)
>
> audit_log_proctitle();
>
> + audit_log_contid(context, audit_get_contid(current));
> +
> /* Send end of event record to help user space know we are finished */
> ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
> if (ab)
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH 2/4] pid: add pidfd_open()
From: Christian Brauner @ 2019-03-27 21:02 UTC (permalink / raw)
To: Jann Horn
Cc: Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, kernel list,
Arnd Bergmann, Kees Cook, Alexey Dobriyan, Thomas Gleixner,
Michael Kerrisk-manpages, Jonathan Kowalski, Dmitry V. Levin,
Andrew Morton, Oleg Nesterov, Nagarathnam Muthusamy, Aleksa Sarai,
Al Viro
In-Reply-To: <CAG48ez2QgRQKYeNDpacLGCOuNKVM1g=1PK3KzzO2Uoyn2cKXaQ@mail.gmail.com>
On Wed, Mar 27, 2019 at 06:07:54PM +0100, Jann Horn wrote:
> On Wed, Mar 27, 2019 at 5:22 PM Christian Brauner <christian@brauner.io> wrote:
> > pidfd_open() allows to retrieve pidfds for processes and removes the
> > dependency of pidfd on procfs. Multiple people have expressed a desire to
> > do this even when pidfd_send_signal() was merged. It is even recorded in
> [...]
> > IF PROCFD_TO_PIDFD is passed as a flag together with a file descriptor to a
> > /proc mount in a given pid namespace and a pidfd pidfd_open() will return a
> > file descriptor to the corresponding /proc/<pid> directory in procfs
> > mounts' pid namespace. pidfd_open() is very careful to verify that the pid
>
> nit: s/mounts'/mount's/
Thanks.
>
> > hasn't been recycled in between.
> > IF PIDFD_TO_PROCFD is passed as a flag together with a file descriptor
> > referencing a /proc/<pid> directory a pidfd referencing the struct pid
> > stashed in /proc/<pid> of the process will be returned.
>
> nit: s/of the process //?
Yes.
>
> > The pidfd_open() syscalls in that manner resembles openat() as it uses a
>
> nit: s/syscalls/syscall/
Thanks.
>
> [...]
> > diff --git a/kernel/pid.c b/kernel/pid.c
> > index 20881598bdfa..c9e24e726aba 100644
> > --- a/kernel/pid.c
> > +++ b/kernel/pid.c
> [...]
> > +static struct file *pidfd_open_proc_pid(const struct file *procf, pid_t pid,
> > + const struct pid *pidfd_pid)
> > +{
> > + char name[11]; /* int to strlen + \0 */
>
> nit: The comment is a bit off; an unconstrained int needs 1+10+1
> bytes, I think? minus sign, 10 digits, nullbyte? But of course that
> can't actually happen here.
Yes, the comment is misleading.
>
> > + struct file *file;
> > + struct pid *proc_pid;
> > +
> > + snprintf(name, sizeof(name), "%d", pid);
> > + file = file_open_root(procf->f_path.dentry, procf->f_path.mnt, name,
> > + O_DIRECTORY | O_NOFOLLOW, 0);
>
> Maybe explicitly write the implied O_RDONLY (which is 0) here for clarity?
Yes.
>
> [...]
> > +static int pidfd_to_procfd(pid_t pid, int procfd, int pidfd)
> > +{
> > + long fd;
> > + pid_t ns_pid;
> > + struct fd fdproc, fdpid;
> > + struct file *file = NULL;
> > + struct pid *pidfd_pid = NULL;
> > + struct pid_namespace *proc_pid_ns = NULL;
> > +
> > + fdproc = fdget(procfd);
> > + if (!fdproc.file)
> > + return -EBADF;
> > +
> > + fdpid = fdget(pidfd);
> > + if (!fdpid.file) {
> > + fdput(fdpid);
>
> Typo: s/fdput(fdpid)/fdput(fdproc)/
Good catch!
>
> [...]
> > +SYSCALL_DEFINE4(pidfd_open, pid_t, pid, int, procfd, int, pidfd, unsigned int,
> > + flags)
> [...]
> > + if (!flags) {
> [...]
> > + rcu_read_lock();
> > + pidfd_pid = get_pid(find_pid_ns(pid, task_active_pid_ns(current)));
> > + rcu_read_unlock();
>
> The previous three lines are equivalent to `pidfd_pid = find_get_pid(pid)`.
Perfect, will replace.
>
> > + fd = pidfd_create_fd(pidfd_pid, O_CLOEXEC);
>
> Nit: You could hardcode O_CLOEXEC in pidfd_create_fd() and get rid of
> the second function argument if you want to.
Hm, let me rename this to pidfd_create_cloexec(pidfd_pid) then.
>
> > + put_pid(pidfd_pid);
> > + } else if (flags & PIDFD_TO_PROCFD) {
> [...]
> > + fd = pidfd_to_procfd(pid, procfd, pidfd);
>
> The `pid` argument of pidfd_to_procfd() looks unused, maybe it makes
> sense to get rid of that?
Yes.
^ permalink raw reply
* Re: [PATCH ghak90 V5 05/10] audit: add containerid support for ptrace and signals
From: Ondrej Mosnacek @ 2019-03-27 21:17 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <a5a612bff629574cf4b678e7c7a9315e0e60a27c.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:34 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Add audit container identifier support to ptrace and signals. In
> particular, the "ref" field provides a way to label the auxiliary record
> to which it is associated.
>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> Acked-by: Serge Hallyn <serge@hallyn.com>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> ---
> include/linux/audit.h | 1 +
> kernel/audit.c | 2 ++
> kernel/audit.h | 2 ++
> kernel/auditsc.c | 23 +++++++++++++++++------
> 4 files changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 43438192ca2a..ebd6625ca80e 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -35,6 +35,7 @@ struct audit_sig_info {
> uid_t uid;
> pid_t pid;
> char ctx[0];
> + u64 cid;
> };
It seems like this structure implicitly defines the format of some
message that is sent to userspace... If so, how will userspace detect
that a new format (including the cid) is being used? Even assuming the
fixed order as pointed out by Neil, the message still seems to be
variable-sized so userspace cannot even use the length to infer that.
Am I missing something here? (I hope I am :)
>
> struct audit_buffer;
> diff --git a/kernel/audit.c b/kernel/audit.c
> index 8cc0e88d7f2a..cfa659b3f6c4 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -138,6 +138,7 @@ struct audit_net {
> kuid_t audit_sig_uid = INVALID_UID;
> pid_t audit_sig_pid = -1;
> u32 audit_sig_sid = 0;
> +u64 audit_sig_cid = AUDIT_CID_UNSET;
>
> /* Records can be lost in several ways:
> 0) [suppressed in audit_alloc]
> @@ -1515,6 +1516,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
> memcpy(sig_data->ctx, ctx, len);
> security_release_secctx(ctx, len);
> }
> + sig_data->cid = audit_sig_cid;
> audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0,
> sig_data, sizeof(*sig_data) + len);
> kfree(sig_data);
> diff --git a/kernel/audit.h b/kernel/audit.h
> index c00e2ee3c6b3..c5ac6436317e 100644
> --- a/kernel/audit.h
> +++ b/kernel/audit.h
> @@ -148,6 +148,7 @@ struct audit_context {
> kuid_t target_uid;
> unsigned int target_sessionid;
> u32 target_sid;
> + u64 target_cid;
> char target_comm[TASK_COMM_LEN];
>
> struct audit_tree_refs *trees, *first_trees;
> @@ -344,6 +345,7 @@ extern void audit_filter_inodes(struct task_struct *tsk,
> extern pid_t audit_sig_pid;
> extern kuid_t audit_sig_uid;
> extern u32 audit_sig_sid;
> +extern u64 audit_sig_cid;
>
> extern int audit_filter(int msgtype, unsigned int listtype);
>
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index a8c8b44b954d..f04e115df5dc 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -113,6 +113,7 @@ struct audit_aux_data_pids {
> kuid_t target_uid[AUDIT_AUX_PIDS];
> unsigned int target_sessionid[AUDIT_AUX_PIDS];
> u32 target_sid[AUDIT_AUX_PIDS];
> + u64 target_cid[AUDIT_AUX_PIDS];
> char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN];
> int pid_count;
> };
> @@ -1514,7 +1515,7 @@ static void audit_log_exit(void)
> for (aux = context->aux_pids; aux; aux = aux->next) {
> struct audit_aux_data_pids *axs = (void *)aux;
>
> - for (i = 0; i < axs->pid_count; i++)
> + for (i = 0; i < axs->pid_count; i++) {
> if (audit_log_pid_context(context, axs->target_pid[i],
> axs->target_auid[i],
> axs->target_uid[i],
> @@ -1522,14 +1523,20 @@ static void audit_log_exit(void)
> axs->target_sid[i],
> axs->target_comm[i]))
> call_panic = 1;
> + audit_log_contid(context, axs->target_cid[i]);
> + }
> }
>
> - if (context->target_pid &&
> - audit_log_pid_context(context, context->target_pid,
> - context->target_auid, context->target_uid,
> - context->target_sessionid,
> - context->target_sid, context->target_comm))
> + if (context->target_pid) {
> + if (audit_log_pid_context(context, context->target_pid,
> + context->target_auid,
> + context->target_uid,
> + context->target_sessionid,
> + context->target_sid,
> + context->target_comm))
> call_panic = 1;
> + audit_log_contid(context, context->target_cid);
> + }
>
> if (context->pwd.dentry && context->pwd.mnt) {
> ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
> @@ -2360,6 +2367,7 @@ void __audit_ptrace(struct task_struct *t)
> context->target_uid = task_uid(t);
> context->target_sessionid = audit_get_sessionid(t);
> security_task_getsecid(t, &context->target_sid);
> + context->target_cid = audit_get_contid(t);
> memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
> }
>
> @@ -2387,6 +2395,7 @@ int audit_signal_info(int sig, struct task_struct *t)
> else
> audit_sig_uid = uid;
> security_task_getsecid(current, &audit_sig_sid);
> + audit_sig_cid = audit_get_contid(current);
> }
>
> if (!audit_signals || audit_dummy_context())
> @@ -2400,6 +2409,7 @@ int audit_signal_info(int sig, struct task_struct *t)
> ctx->target_uid = t_uid;
> ctx->target_sessionid = audit_get_sessionid(t);
> security_task_getsecid(t, &ctx->target_sid);
> + ctx->target_cid = audit_get_contid(t);
> memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
> return 0;
> }
> @@ -2421,6 +2431,7 @@ int audit_signal_info(int sig, struct task_struct *t)
> axp->target_uid[axp->pid_count] = t_uid;
> axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
> security_task_getsecid(t, &axp->target_sid[axp->pid_count]);
> + axp->target_cid[axp->pid_count] = audit_get_contid(t);
> memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
> axp->pid_count++;
>
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH ghak90 V5 06/10] audit: add support for non-syscall auxiliary records
From: Ondrej Mosnacek @ 2019-03-27 21:22 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <700e4313571f86cc0cdcb7310fbe060f02c7c2db.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:34 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Standalone audit records have the timestamp and serial number generated
> on the fly and as such are unique, making them standalone. This new
> function audit_alloc_local() generates a local audit context that will
> be used only for a standalone record and its auxiliary record(s). The
> context is discarded immediately after the local associated records are
> produced.
>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
> Acked-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> include/linux/audit.h | 8 ++++++++
> kernel/audit.h | 1 +
> kernel/auditsc.c | 35 ++++++++++++++++++++++++++++++-----
> 3 files changed, 39 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index ebd6625ca80e..6db5aba7cc01 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -285,6 +285,8 @@ static inline void audit_log_contid(struct audit_context *context, u64 contid)
>
> /* These are defined in auditsc.c */
> /* Public API */
> +extern struct audit_context *audit_alloc_local(gfp_t gfpflags);
> +extern void audit_free_context(struct audit_context *context);
> extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
> unsigned long a2, unsigned long a3);
> extern void __audit_syscall_exit(int ret_success, long ret_value);
> @@ -512,6 +514,12 @@ static inline void audit_fanotify(unsigned int response)
> extern int audit_n_rules;
> extern int audit_signals;
> #else /* CONFIG_AUDITSYSCALL */
> +static inline struct audit_context *audit_alloc_local(gfp_t gfpflags)
> +{
> + return NULL;
> +}
> +static inline void audit_free_context(struct audit_context *context)
> +{ }
> static inline void audit_syscall_entry(int major, unsigned long a0,
> unsigned long a1, unsigned long a2,
> unsigned long a3)
> diff --git a/kernel/audit.h b/kernel/audit.h
> index c5ac6436317e..2a1a8b8a8019 100644
> --- a/kernel/audit.h
> +++ b/kernel/audit.h
> @@ -111,6 +111,7 @@ struct audit_proctitle {
> struct audit_context {
> int dummy; /* must be the first element */
> int in_syscall; /* 1 if task is in a syscall */
> + bool local; /* local context needed */
> enum audit_state state, current_state;
> unsigned int serial; /* serial number for record */
> int major; /* syscall number */
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index f04e115df5dc..aa5d13b4fbbb 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -872,11 +872,13 @@ static inline void audit_free_aux(struct audit_context *context)
> }
> }
>
> -static inline struct audit_context *audit_alloc_context(enum audit_state state)
> +static inline struct audit_context *audit_alloc_context(enum audit_state state,
> + gfp_t gfpflags)
> {
> struct audit_context *context;
>
> - context = kzalloc(sizeof(*context), GFP_KERNEL);
> + /* We can be called in atomic context via audit_tg() */
> + context = kzalloc(sizeof(*context), gfpflags);
> if (!context)
> return NULL;
> context->state = state;
> @@ -912,7 +914,8 @@ int audit_alloc_syscall(struct task_struct *tsk)
> return 0;
> }
>
> - if (!(context = audit_alloc_context(state))) {
> + context = audit_alloc_context(state, GFP_KERNEL);
> + if (!context) {
> kfree(key);
> audit_log_lost("out of memory in audit_alloc_syscall");
> return -ENOMEM;
> @@ -924,8 +927,29 @@ int audit_alloc_syscall(struct task_struct *tsk)
> return 0;
> }
>
> -static inline void audit_free_context(struct audit_context *context)
> +struct audit_context *audit_alloc_local(gfp_t gfpflags)
> {
> + struct audit_context *context = NULL;
> +
> + if (!audit_ever_enabled)
> + goto out; /* Return if not auditing. */
> + context = audit_alloc_context(AUDIT_RECORD_CONTEXT, gfpflags);
> + if (!context) {
> + audit_log_lost("out of memory in audit_alloc_local");
> + goto out;
> + }
> + context->serial = audit_serial();
> + ktime_get_coarse_real_ts64(&context->ctime);
> + context->local = true;
> +out:
> + return context;
> +}
> +EXPORT_SYMBOL(audit_alloc_local);
> +
> +void audit_free_context(struct audit_context *context)
> +{
> + if (!context)
> + return;
> audit_free_names(context);
> unroll_tree_refs(context, NULL, 0);
> free_tree_refs(context);
> @@ -935,6 +959,7 @@ static inline void audit_free_context(struct audit_context *context)
> audit_proctitle_free(context);
> kfree(context);
> }
> +EXPORT_SYMBOL(audit_free_context);
>
> static int audit_log_pid_context(struct audit_context *context, pid_t pid,
> kuid_t auid, kuid_t uid, unsigned int sessionid,
> @@ -2163,7 +2188,7 @@ void __audit_inode_child(struct inode *parent,
> int auditsc_get_stamp(struct audit_context *ctx,
> struct timespec64 *t, unsigned int *serial)
> {
> - if (!ctx->in_syscall)
> + if (!ctx->in_syscall && !ctx->local)
> return 0;
> if (!ctx->serial)
> ctx->serial = audit_serial();
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH ghak90 V5 07/10] audit: add containerid support for user records
From: Ondrej Mosnacek @ 2019-03-27 21:30 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <e724de2037db0fd72fef7154c83615a742417d31.1552665316.git.rgb@redhat.com>
On Fri, Mar 15, 2019 at 7:34 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> Add audit container identifier auxiliary record to user event standalone
> records.
>
> Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> kernel/audit.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/kernel/audit.c b/kernel/audit.c
> index cfa659b3f6c4..cf448599ef34 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -1142,12 +1142,6 @@ static void audit_log_common_recv_msg(struct audit_context *context,
> audit_log_task_context(*ab);
> }
>
> -static inline void audit_log_user_recv_msg(struct audit_buffer **ab,
> - u16 msg_type)
> -{
> - audit_log_common_recv_msg(NULL, ab, msg_type);
> -}
> -
> int is_audit_feature_set(int i)
> {
> return af.features & AUDIT_FEATURE_TO_MASK(i);
> @@ -1409,13 +1403,16 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
>
> err = audit_filter(msg_type, AUDIT_FILTER_USER);
> if (err == 1) { /* match or error */
> + struct audit_context *context;
> +
> err = 0;
> if (msg_type == AUDIT_USER_TTY) {
> err = tty_audit_push();
> if (err)
> break;
> }
> - audit_log_user_recv_msg(&ab, msg_type);
> + context = audit_alloc_local(GFP_KERNEL);
> + audit_log_common_recv_msg(context, &ab, msg_type);
> if (msg_type != AUDIT_USER_TTY)
> audit_log_format(ab, " msg='%.*s'",
> AUDIT_MESSAGE_TEXT_MAX,
> @@ -1431,6 +1428,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
> audit_log_n_untrustedstring(ab, data, size);
> }
> audit_log_end(ab);
> + audit_log_contid(context, audit_get_contid(current));
> + audit_free_context(context);
> }
> break;
> case AUDIT_ADD_RULE:
> --
> 1.8.3.1
>
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH 2/4] pid: add pidfd_open()
From: Christian Brauner @ 2019-03-27 21:34 UTC (permalink / raw)
To: Jonathan Kowalski
Cc: Jann Horn, Konstantin Khlebnikov, Andy Lutomirski, David Howells,
Serge E. Hallyn, Eric W. Biederman, Linux API, linux-kernel,
Arnd Bergmann, Kees Cook, Alexey Dobriyan, Thomas Gleixner,
Michael Kerrisk-manpages, Dmitry V. Levin, Andrew Morton,
Oleg Nesterov, Nagarathnam Muthusamy, Aleksa Sarai, Al Viro
In-Reply-To: <CAGLj2rG8xMMt8-+r-pauA_qi4aun2y_kBZMCZGkXWQsKJrb-Ww@mail.gmail.com>
On Wed, Mar 27, 2019 at 07:38:13PM +0000, Jonathan Kowalski wrote:
> Christian,
>
> Giving this some thought, it looks fine to me, but I am not convinced
> this should take a pid argument. I much prefer if obtaining a pidfd
The signatures for pidfd_open() you're suggesting are conflicting. Even
taking into account that you're referring to Andy's version:
int procfd = pidfd_open(procrootfd, pidfd, PIDFD_TO_PROCFD); //name
the "not convinced this should take a pid argument" confuses me wrt to
the proposed second signature:
pidfd_open(pid_t pid, int ns, unsigned int flags);
> goes in the translation stuff, that has very real usecases, and you
> would end up duplicating the same thing over in two places.
>
> If the /proc/<PID> dir fd of a pidfd is relative to its procrootfd, a
> pid's pidfd is also relative to its namespace. Currently, you change
Pass a /proc/<pid> file descriptor that you have been given access to to
pidfd_open() and retrieve a pidfd to that process.
> things which would mean I now have to setns and spawn a child that
> passes me back the pidfd for a pid in the said namespace.
There are two scenarios:
- you're in a cousin pid namespace
- you're in an ancestor pid namespace
If you're in an ancestor pid namespace you can just get a pidfd based on
the pid of the process in your namespace. If you're in a cousin
pid namespace it seems reasonable that you have to do a setns to get a
pidfd. After all, you're not able to see any pids in there by default.
If you want pidfd to a cousin pid namespace prove that you can access it
by attaching to the owning user namespace of the pid namespace and get
your pidfd.
>
> I prefer Andy's version of
>
> int procrootfd = open("/proc", O_DIRECTORY | O_RDONLY);
> int procfd = pidfd_open(procrootfd, pidfd, PIDFD_TO_PROCFD); //name
> this procfd_open, maybe
>
> This is just a nicer race free openat.
>
> But, Joel and you agreed that it makes sense to split out the
> translation stuff out of pidfds.
I don't quite remember that part.
I honestly think that the version proposed here covers for the most part
what we want and provides a decent compromise by avoiding ioctl()s for
the translation bits, allows us to not split this into multiple
syscalls, and also allows retrieving pidfds from other pid namespaces
provided that one has gotten access to a file descriptor for
/proc/<pid>. If really needed at some point - I doubt we will - you
can extend pidfd_open() to take the flag CLONE_NEWPID:
int pidfd = pidfd_open(pid, pid_ns_fd, -1, CLONE_NEWPID);
and get pidfd for another pid namespace back.
Christian
>
> My suggestion would be to extend ioctl_ns(2) then. It already provides
> a (rather clumsy) mechanism to determine the relationship by comparing
> st_dev and st_ino, which userspace can do itself for two namespace
> descriptors. For translation, you can extend this namespace ioctl
> (there is one to get a owner UID, you could add one to get a relative
> PID).
>
> Then, your pidfd call will be:
>
> pidfd_open(pid_t pid, int ns, unsigned int flags);
>
> You would also be able to compile out anything procfs related (include
> the new API to procfs dir fd translation), otherwise, the way to open
> pidfds is in this call, and without CONFIG_PROC_FS=Y, this is as good
> as pidfd_open(pid_t pid) (of which a better version I propose above).
> The new API should be its own thing, and the procfs translation tuple
> its own thing, tied to the proc fs option. pidfds need not have any
> relation to /proc.
They don't have a relation to procfs right now in this syscall. Works
fine without procfs. That's the point.
>
> For this procfd conversion system call, I would also lift any
> namespace related restrictions if you are planning to, given the
> constraint that I can only open a pidfd from an ancestor namespace
> with the new pidfd_open system call, or acquire it through a
> cooperative userspace process by fd passing otherwise, and I need the
> /proc root fd, having both only permits me to open the said process's
> /proc/<PID> dir fd subject to normal access restrictions. This means
> the simplified procfd_open can be used to do metadata access without
> even talking of PIDs at all, your process could live in its own world
> and, given it has the authority to open the /proc directory, be able
> to purely collect metadata based on the two file descriptors it is
> given.
>
> Once you have the restriction in the same call that allows you to open
> a pidfd for an addressable PID from the given namespace fd, you can
> finally remove the restriction to signal across namespaces once the
> siginfo stuff is sorted out, as that will only work when you
> explicitly push the fd into a sandbox, the process cannot get it out
> of thin air on its own (and you already mentioned it has nothing to do
> with security). What I do worry about is one can use NS_GET_PARENT
> ioctl to get the parent pidns if the owning userns is the same, and
> just passing that gives me back a pidfd for the task. **So, you might
> want to add the constraint that the PID is actually reachable by the
> current task as well, apart from being reachable in the passed in
> namespace.**
>
> Lastly, I also see no need of /proc/<PID> dir fd to pidfd conversion,
> I would even recommend getting rid of that, so we only have one type
> of pidfd, the anon inode one. What is the usecase behind that? It
> would only be needed if you did not have a way to be able to metadata
> access through a pidfd, which would be the case only prior to this
> patch.
>
> I think this would simplify a lot of things, and ioctl_ns(2) is
> probably already the place to do comparison operations and query
> operations on hierarichal namespaces, just adding the relative PID bit
> will make it gain feature parity with translate_pid.
^ permalink raw reply
* Re: [PATCH ghak90 V5 08/10] audit: add containerid filtering
From: Ondrej Mosnacek @ 2019-03-27 21:41 UTC (permalink / raw)
To: Richard Guy Briggs
Cc: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel, Paul Moore, Steve Grubb,
David Howells, Simo Sorce, Eric Paris, Serge E. Hallyn,
Eric W . Biederman, nhorman
In-Reply-To: <20190318234716.4moownrpylloiu3g@madcap2.tricolour.ca>
On Tue, Mar 19, 2019 at 12:47 AM Richard Guy Briggs <rgb@redhat.com> wrote:
> On 2019-03-18 21:02, Ondrej Mosnacek wrote:
> > On Fri, Mar 15, 2019 at 7:35 PM Richard Guy Briggs <rgb@redhat.com> wrote:
> > >
> > > Implement audit container identifier filtering using the AUDIT_CONTID
> > > field name to send an 8-character string representing a u64 since the
> > > value field is only u32.
> > >
> > > Sending it as two u32 was considered, but gathering and comparing two
> > > fields was more complex.
> > >
> > > The feature indicator is AUDIT_FEATURE_BITMAP_CONTAINERID.
> > >
> > > See: https://github.com/linux-audit/audit-kernel/issues/91
> > > See: https://github.com/linux-audit/audit-userspace/issues/40
> > > 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>
> > > Acked-by: Serge Hallyn <serge@hallyn.com>
> > > ---
> > > include/linux/audit.h | 1 +
> > > include/uapi/linux/audit.h | 5 ++++-
> > > kernel/audit.h | 1 +
> > > kernel/auditfilter.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++
> > > kernel/auditsc.c | 3 +++
> > > 5 files changed, 56 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/include/linux/audit.h b/include/linux/audit.h
> > > index 6db5aba7cc01..fa19fa408931 100644
> > > --- a/include/linux/audit.h
> > > +++ b/include/linux/audit.h
> > > @@ -77,6 +77,7 @@ struct audit_field {
> > > u32 type;
> > > union {
> > > u32 val;
> > > + u64 val64;
> > > kuid_t uid;
> > > kgid_t gid;
> > > struct {
> > > diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> > > index a6383e28b2c8..741ab6f38294 100644
> > > --- a/include/uapi/linux/audit.h
> > > +++ b/include/uapi/linux/audit.h
> > > @@ -265,6 +265,7 @@
> > > #define AUDIT_LOGINUID_SET 24
> > > #define AUDIT_SESSIONID 25 /* Session ID */
> > > #define AUDIT_FSTYPE 26 /* FileSystem Type */
> > > +#define AUDIT_CONTID 27 /* Container ID */
> > >
> > > /* These are ONLY useful when checking
> > > * at syscall exit time (AUDIT_AT_EXIT). */
> > > @@ -345,6 +346,7 @@ enum {
> > > #define AUDIT_FEATURE_BITMAP_SESSIONID_FILTER 0x00000010
> > > #define AUDIT_FEATURE_BITMAP_LOST_RESET 0x00000020
> > > #define AUDIT_FEATURE_BITMAP_FILTER_FS 0x00000040
> > > +#define AUDIT_FEATURE_BITMAP_CONTAINERID 0x00000080
> > >
> > > #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \
> > > AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \
> > > @@ -352,7 +354,8 @@ enum {
> > > AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND | \
> > > AUDIT_FEATURE_BITMAP_SESSIONID_FILTER | \
> > > AUDIT_FEATURE_BITMAP_LOST_RESET | \
> > > - AUDIT_FEATURE_BITMAP_FILTER_FS)
> > > + AUDIT_FEATURE_BITMAP_FILTER_FS | \
> > > + AUDIT_FEATURE_BITMAP_CONTAINERID)
> > >
> > > /* deprecated: AUDIT_VERSION_* */
> > > #define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL
> > > diff --git a/kernel/audit.h b/kernel/audit.h
> > > index 2a1a8b8a8019..3a40b608bf8d 100644
> > > --- a/kernel/audit.h
> > > +++ b/kernel/audit.h
> > > @@ -230,6 +230,7 @@ static inline int audit_hash_ino(u32 ino)
> > >
> > > extern int audit_match_class(int class, unsigned syscall);
> > > extern int audit_comparator(const u32 left, const u32 op, const u32 right);
> > > +extern int audit_comparator64(const u64 left, const u32 op, const u64 right);
> > > extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right);
> > > extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right);
> > > extern int parent_len(const char *path);
> > > diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
> > > index add360b46b38..516b8e58959e 100644
> > > --- a/kernel/auditfilter.c
> > > +++ b/kernel/auditfilter.c
> > > @@ -410,6 +410,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
> > > /* FALL THROUGH */
> > > case AUDIT_ARCH:
> > > case AUDIT_FSTYPE:
> > > + case AUDIT_CONTID:
> > > if (f->op != Audit_not_equal && f->op != Audit_equal)
> > > return -EINVAL;
> > > break;
> > > @@ -582,6 +583,14 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
> > > }
> > > entry->rule.exe = audit_mark;
> > > break;
> > > + case AUDIT_CONTID:
> > > + if (f->val != sizeof(u64))
> > > + goto exit_free;
> > > + str = audit_unpack_string(&bufp, &remain, f->val);
> > > + if (IS_ERR(str))
> > > + goto exit_free;
> > > + f->val64 = ((u64 *)str)[0];
> > > + break;
> > > }
> > > }
> > >
> > > @@ -664,6 +673,11 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
> > > data->buflen += data->values[i] =
> > > audit_pack_string(&bufp, audit_mark_path(krule->exe));
> > > break;
> > > + case AUDIT_CONTID:
> > > + data->buflen += data->values[i] = sizeof(u64);
> > > + for (i = 0; i < sizeof(u64); i++)
> > > + ((char *)bufp)[i] = ((char *)&f->val64)[i];
> >
> > How about just:
> >
> > memcpy(bufp, &f->val64, sizeof(u64));
> >
> > instead of the awkward for loop? It is simpler and also more in line
> > with the code in audit_pack_string().
>
> I'll grant you that. I'll use that instead.
Assuming the above tweak will be done, you can add:
Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
>
> > Also, doesn't this loop interfere with the outer loop that also uses
> > 'i' as the control variable?
>
> Yes, it does. Thank you for catching that. Doing a auditctl -l reveals
> this.
>
> > > + break;
> > > case AUDIT_LOGINUID_SET:
> > > if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
> > > data->fields[i] = AUDIT_LOGINUID;
> > > @@ -750,6 +764,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
> > > if (!gid_eq(a->fields[i].gid, b->fields[i].gid))
> > > return 1;
> > > break;
> > > + case AUDIT_CONTID:
> > > + if (a->fields[i].val64 != b->fields[i].val64)
> > > + return 1;
> > > + break;
> > > default:
> > > if (a->fields[i].val != b->fields[i].val)
> > > return 1;
> > > @@ -1206,6 +1224,31 @@ int audit_comparator(u32 left, u32 op, u32 right)
> > > }
> > > }
> > >
> > > +int audit_comparator64(u64 left, u32 op, u64 right)
> > > +{
> > > + switch (op) {
> > > + case Audit_equal:
> > > + return (left == right);
> > > + case Audit_not_equal:
> > > + return (left != right);
> > > + case Audit_lt:
> > > + return (left < right);
> > > + case Audit_le:
> > > + return (left <= right);
> > > + case Audit_gt:
> > > + return (left > right);
> > > + case Audit_ge:
> > > + return (left >= right);
> > > + case Audit_bitmask:
> > > + return (left & right);
> > > + case Audit_bittest:
> > > + return ((left & right) == right);
> > > + default:
> > > + BUG();
> > > + return 0;
> > > + }
> > > +}
> > > +
> > > int audit_uid_comparator(kuid_t left, u32 op, kuid_t right)
> > > {
> > > switch (op) {
> > > @@ -1344,6 +1387,10 @@ int audit_filter(int msgtype, unsigned int listtype)
> > > result = audit_comparator(audit_loginuid_set(current),
> > > f->op, f->val);
> > > break;
> > > + case AUDIT_CONTID:
> > > + result = audit_comparator64(audit_get_contid(current),
> > > + f->op, f->val64);
> > > + break;
> > > case AUDIT_MSGTYPE:
> > > result = audit_comparator(msgtype, f->op, f->val);
> > > break;
> > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > index aa5d13b4fbbb..2d74238e9638 100644
> > > --- a/kernel/auditsc.c
> > > +++ b/kernel/auditsc.c
> > > @@ -616,6 +616,9 @@ static int audit_filter_rules(struct task_struct *tsk,
> > > case AUDIT_LOGINUID_SET:
> > > result = audit_comparator(audit_loginuid_set(tsk), f->op, f->val);
> > > break;
> > > + case AUDIT_CONTID:
> > > + result = audit_comparator64(audit_get_contid(tsk), f->op, f->val64);
> > > + break;
> > > case AUDIT_SUBJ_USER:
> > > case AUDIT_SUBJ_ROLE:
> > > case AUDIT_SUBJ_TYPE:
> > > --
> > > 1.8.3.1
> > >
> >
> >
> > --
> > Ondrej Mosnacek <omosnace at redhat dot com>
> > Associate Software Engineer, Security Technologies
> > Red Hat, Inc.
>
> - RGB
>
> --
> Richard Guy Briggs <rgb@redhat.com>
> Sr. S/W Engineer, Kernel Security, Base Operating Systems
> Remote, Ottawa, Red Hat Canada
> IRC: rgb, SunRaycer
> Voice: +1.647.777.2635, Internal: (81) 32635
--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.
^ permalink raw reply
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