[PATCH 3] utrace: introduce ENGINE_LSM_ flags

2010-07-07 Thread Oleg Nesterov
Introduce ENGINE_LSM_TRACE and ENGINE_LSM_TRACE_CAP bits for
utrace_unsafe_exec(). These bit should be set when we attach the
new engine by user request.

Note: we use engine-flags and task-utrace_flags, this doesn't
really matter. The only important point is: somehow utrace_engine
should have the security info which we do not currently have.

Note!! The next patches try to convert ptrace-utrace, but
ptrace is only used for example. gdbstub or whatever has the same
security problems and needs.

---

 kernel/utrace.c |   16 +---
 1 file changed, 13 insertions(+), 3 deletions(-)

--- RHEL6/kernel/utrace.c~3_ENGINE_LSM_FLAGS2010-07-06 23:55:14.0 
+0200
+++ RHEL6/kernel/utrace.c   2010-07-07 00:48:09.0 +0200
@@ -460,7 +460,11 @@ static void put_detached_list(struct lis
  */
 #define ENGINE_STOP(1UL  _UTRACE_NEVENTS)
 
-#define ENGINE_EXTRA_FLAGS (ENGINE_STOP)
+#define ENGINE_LSM_TRACE   (1UL  (_UTRACE_NEVENTS + 1))
+#define ENGINE_LSM_TRACE_CAP   (1UL  (_UTRACE_NEVENTS + 2))
+#define ENGINE_LSM_MASK(ENGINE_LSM_TRACE | 
ENGINE_LSM_TRACE_CAP)
+
+#define ENGINE_EXTRA_FLAGS (ENGINE_STOP | ENGINE_LSM_MASK)
 
 static void mark_engine_wants_stop(struct task_struct *task,
   struct utrace_engine *engine)
@@ -2457,9 +2461,15 @@ int utrace_unsafe_exec(struct task_struc
 {
int unsafe = 0;
 
-   if (task-ptrace  PT_PTRACE_CAP)
+   if (task-utrace_flags  ENGINE_LSM_TRACE)
+   unsafe = LSM_UNSAFE_PTRACE;
+   else if (task-utrace_flags  ENGINE_LSM_TRACE_CAP)
unsafe = LSM_UNSAFE_PTRACE_CAP;
-   else if (task-ptrace)
+
+   if (task-ptrace  PT_PTRACE_CAP) {
+   if (!unsafe)
+   unsafe = LSM_UNSAFE_PTRACE_CAP;
+   } else if (task-ptrace)
unsafe = LSM_UNSAFE_PTRACE;
 
return unsafe;



[PATCH 1] utrace: introduce ENGINE_EXTRA_FLAGS

2010-07-07 Thread Oleg Nesterov
The only patch which probably makes sense anyway.

Currently utrace assumes that task-utrace_flags can have only one
special bit, ENGINE_STOP. But it make sense to add more special bits.

For example, ptrace-utrace. It still uses task-ptrace for PT_PTRACE_CAP
and PT_UTRACED (the latter one is only needed to indicate that this task
is re-parented by ptrace). We can move them both into -utrace_flags.

Introduce ENGINE_EXTRA_FLAGS == ENGINE_STOP and change utrace_set_events()
to use it. It is the only helper which changes target-utrace_flags and
engine-flags and needs the fix. utrace_reset() is OK, it just OR's all
engine-flags into task-utrace_flags.

See also the next patches.

---

 kernel/utrace.c |8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

--- RHEL6/kernel/utrace.c~1_NONEVENT_BITS   2010-01-31 17:01:59.0 
+0100
+++ RHEL6/kernel/utrace.c   2010-07-06 22:47:28.0 +0200
@@ -460,6 +460,8 @@ static void put_detached_list(struct lis
  */
 #define ENGINE_STOP(1UL  _UTRACE_NEVENTS)
 
+#define ENGINE_EXTRA_FLAGS (ENGINE_STOP)
+
 static void mark_engine_wants_stop(struct task_struct *task,
   struct utrace_engine *engine)
 {
@@ -530,14 +532,14 @@ int utrace_set_events(struct task_struct
 * We just ignore the internal bit, so callers can use
 * engine-flags to seed bitwise ops for our argument.
 */
-   events = ~ENGINE_STOP;
+   events = ~ENGINE_EXTRA_FLAGS;
 
utrace = get_utrace_lock(target, engine, true);
if (unlikely(IS_ERR(utrace)))
return PTR_ERR(utrace);
 
old_utrace_flags = target-utrace_flags;
-   old_flags = engine-flags  ~ENGINE_STOP;
+   old_flags = engine-flags  ~ENGINE_EXTRA_FLAGS;
 
if (target-exit_state 
(((events  ~old_flags)  _UTRACE_DEATH_EVENTS) ||
@@ -569,7 +571,7 @@ int utrace_set_events(struct task_struct
read_unlock(tasklist_lock);
}
 
-   engine-flags = events | (engine-flags  ENGINE_STOP);
+   engine-flags = events | (engine-flags  ENGINE_EXTRA_FLAGS);
target-utrace_flags |= events;
 
if ((events  UTRACE_EVENT_SYSCALL) 



[PATCH 5] utrace: convert ptrace to use utrace_set_caps()

2010-07-07 Thread Oleg Nesterov
Convert ptrace to use utrace_set_caps(). ptrace_report_clone() uses
utrace_get_caps() and PT_UTRACED instead of parent-ptrace.

Henceforth task_struct-ptrace is only used for PT_UTRACED, and this
bit can be moved into ENGINE_EXTRA_FLAGS.

utrace_unsafe_exec() doesn't need to play with task-ptrace any longer.

---

 kernel/ptrace-utrace.c |   18 --
 kernel/utrace.c|6 --
 2 files changed, 8 insertions(+), 16 deletions(-)

--- RHEL6/kernel/ptrace-utrace.c~5_CONVERT_PTRACE   2010-05-20 
12:40:50.0 +0200
+++ RHEL6/kernel/ptrace-utrace.c2010-07-07 02:59:52.0 +0200
@@ -198,7 +198,7 @@ static inline int ptrace_set_events(stru
  * Attach a utrace engine for ptrace and set up its event mask.
  * Returns error code or 0 on success.
  */
-static int ptrace_attach_task(struct task_struct *tracee, int options)
+static int ptrace_attach_task(struct task_struct *tracee, int options, int 
caps)
 {
struct utrace_engine *engine;
int err;
@@ -295,13 +295,13 @@ static u32 ptrace_report_exit(u32 action
 }
 
 static void ptrace_clone_attach(struct task_struct *child,
-   int options)
+   int options, int caps)
 {
struct task_struct *parent = current;
struct task_struct *tracer;
bool abort = true;
 
-   if (unlikely(ptrace_attach_task(child, options))) {
+   if (unlikely(ptrace_attach_task(child, options, caps))) {
WARN_ON(1);
return;
}
@@ -309,7 +309,7 @@ static void ptrace_clone_attach(struct t
write_lock_irq(tasklist_lock);
tracer = parent-parent;
if (!(tracer-flags  PF_EXITING)  parent-ptrace) {
-   child-ptrace = parent-ptrace;
+   child-ptrace = PT_UTRACED;
__ptrace_link(child, tracer);
abort = false;
}
@@ -351,7 +351,8 @@ static u32 ptrace_report_clone(u32 actio
 */
if ((event  event != PTRACE_EVENT_VFORK_DONE) ||
(clone_flags  CLONE_PTRACE))
-   ptrace_clone_attach(child, ctx-options);
+   ptrace_clone_attach(child, ctx-options,
+   utrace_get_caps(engine));
 
if (!event)
return UTRACE_RESUME;
@@ -632,7 +633,7 @@ int ptrace_attach(struct task_struct *ta
if (retval)
goto unlock_creds;
 
-   retval = ptrace_attach_task(task, 0);
+   retval = ptrace_attach_task(task, 0, capable(CAP_SYS_PTRACE));
if (unlikely(retval))
goto unlock_creds;
 
@@ -643,9 +644,6 @@ int ptrace_attach(struct task_struct *ta
 
BUG_ON(task-ptrace);
task-ptrace = PT_UTRACED;
-   if (capable(CAP_SYS_PTRACE))
-   task-ptrace |= PT_PTRACE_CAP;
-
__ptrace_link(task, current);
send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
 
@@ -665,7 +663,7 @@ out:
 int ptrace_traceme(void)
 {
bool detach = true;
-   int ret = ptrace_attach_task(current, 0);
+   int ret = ptrace_attach_task(current, 0, 0);
 
if (unlikely(ret))
return ret;
--- RHEL6/kernel/utrace.c~5_CONVERT_PTRACE  2010-07-07 02:41:31.0 
+0200
+++ RHEL6/kernel/utrace.c   2010-07-07 19:16:19.0 +0200
@@ -2494,11 +2494,5 @@ int utrace_unsafe_exec(struct task_struc
else if (task-utrace_flags  ENGINE_LSM_TRACE_CAP)
unsafe = LSM_UNSAFE_PTRACE_CAP;
 
-   if (task-ptrace  PT_PTRACE_CAP) {
-   if (!unsafe)
-   unsafe = LSM_UNSAFE_PTRACE_CAP;
-   } else if (task-ptrace)
-   unsafe = LSM_UNSAFE_PTRACE;
-
return unsafe;
 }



[PATCH 2] utrace: introduce utrace_unsafe_exec()

2010-07-07 Thread Oleg Nesterov
Introduce utrace_unsafe_exec() used by tracehook_unsafe_exec().
Currently the new helper just copies the old -ptrace logic.

Whatever we do, we need something like this patch. Once we implement
anything which can be used by unprivileged user we should handle the
security problems, in particular we should worry about suid-execs.

---

 include/linux/utrace.h|2 ++
 include/linux/tracehook.h |   10 +++---
 kernel/utrace.c   |   12 
 3 files changed, 21 insertions(+), 3 deletions(-)

--- RHEL6/include/linux/utrace.h~2_UNSAFE_EXEC  2010-01-03 16:53:22.0 
+0100
+++ RHEL6/include/linux/utrace.h2010-07-06 23:43:33.0 +0200
@@ -107,6 +107,8 @@ bool utrace_report_syscall_entry(struct 
 void utrace_report_syscall_exit(struct pt_regs *);
 void utrace_signal_handler(struct task_struct *, int);
 
+int utrace_unsafe_exec(struct task_struct *);
+
 #ifndef CONFIG_UTRACE
 
 /*
--- RHEL6/include/linux/tracehook.h~2_UNSAFE_EXEC   2010-01-03 
16:53:22.0 +0100
+++ RHEL6/include/linux/tracehook.h 2010-07-06 23:47:14.0 +0200
@@ -163,9 +163,13 @@ static inline void tracehook_report_sysc
  */
 static inline int tracehook_unsafe_exec(struct task_struct *task)
 {
-   int unsafe = 0;
-   int ptrace = task_ptrace(task);
-   if (ptrace) {
+   int ptrace, unsafe = 0;
+
+   if (task_utrace_flags(task))
+   return utrace_unsafe_exec(task);
+
+   ptrace = task_ptrace(task);
+   if (ptrace  PT_PTRACED) {
if (ptrace  PT_PTRACE_CAP)
unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
--- RHEL6/kernel/utrace.c~2_UNSAFE_EXEC 2010-07-06 22:47:28.0 +0200
+++ RHEL6/kernel/utrace.c   2010-07-06 23:55:14.0 +0200
@@ -2452,3 +2452,15 @@ void task_utrace_proc_status(struct seq_
 {
seq_printf(m, Utrace:\t%lx\n, p-utrace_flags);
 }
+
+int utrace_unsafe_exec(struct task_struct *task)
+{
+   int unsafe = 0;
+
+   if (task-ptrace  PT_PTRACE_CAP)
+   unsafe = LSM_UNSAFE_PTRACE_CAP;
+   else if (task-ptrace)
+   unsafe = LSM_UNSAFE_PTRACE;
+
+   return unsafe;
+}



Re: [PATCH 0/6] utrace: security problems

2010-07-07 Thread Roland McGrath
As to the unsafe_exec stuff, I'd long figured we would have something just
about like that.  (You might recall that an earlier utrace API had an
unsafe_exec engine callback, which had its own unresolved complications.)

For exec transitions (set-id, file caps, selinux), I'd originally figured
an engine's report_exec could check for changes and decide to detach itself
if appropriate.  We will figure out when we come to it whether that can
really cover all the exec angles or not.  setprocattr is the one other
troublesome wrinkle, which I haven't thought all that much about.

I don't think we need to, nor should, try to tie down the security-related
stuff at the outset.  We can work on prototype engines with the proviso
that they are for root only or for experimentation when one doesn't care
about security issues yet.  When we have at least a couple of different
engines with different access models, we will be better placed to figure
out how to tie in the security issues.

In the long run, the security_ptrace() granularity of hook is probably too
blunt an instrument.  We'll want to contemplate the different kinds of
engines with their different kinds of security-relevant interactions
and decide on security checking models that give the appropriate flexibility.
But it's premature to get into that before we have a bit of an ecosystem of
different sorts of modules to consider concretely.


Thanks,
Roland



Re: [PATCH 0/6] utrace: security problems

2010-07-07 Thread Oleg Nesterov
On 07/07, Roland McGrath wrote:

 For exec transitions (set-id, file caps, selinux), I'd originally figured
 an engine's report_exec could check for changes and decide to detach itself
 if appropriate.

No, it can't. At this point S_ISUID/S_ISGID exid's were already dropped,
or exec can fail before before tracehook_report_exec().

We probably need new hooks, both in LSM and utrace.

 But it's premature to get into that before we have a bit of an ecosystem of
 different sorts of modules to consider concretely.

Yes, agreed, let's forget this for now.

The only question: do you think the trivial 1st patch is correct? Probably
it makes sense anyway (not now, yes). It would be really nice to avoid
using task-ptrace, this is the only old-ptrace-related member from
task_struct we currently use. I regret I didn't think about this when I
added PT_UTRACED.

Oleg.



Re: [PATCH 0/6] utrace: security problems

2010-07-07 Thread Roland McGrath
  For exec transitions (set-id, file caps, selinux), I'd originally figured
  an engine's report_exec could check for changes and decide to detach itself
  if appropriate.
 
 No, it can't. At this point S_ISUID/S_ISGID exid's were already dropped,
 or exec can fail before before tracehook_report_exec().

If an exec fails, nothing changes and there is no security-relevant event
to take notice of.  I don't really follow your other comment.  But ...

 Yes, agreed, let's forget this for now.

Indeed.

 The only question: do you think the trivial 1st patch is correct?

The one that just adds a macro defined to another existing macro?
Any change that preprocesses out to the same code is correct, sure...


Thanks,
Roland