Package: bcfg2-server
Version: 1.0.1-3+squeeze1
Severity: critical
Tags: security, patch, pending

Quoting the upstream announcement (written by Chris St. Pierre):

"We have found a major security flaw in the Trigger plugin that would allow a
malicious user who has root access to a Bcfg2 client to run arbitrary commands
on the server as the user the bcfg2-server process is running as by passing a
malformed UUID.

This is very similar to a flaw discovered last year in a large number of other
plugins; this instance was not fixed at that time because Trigger uses a
different method to invoke external shell commands, and because Trigger
previously hid all errors from trigger scripts, so tests did not find the
issue.  As a side effect of this change, Trigger will begin reporting errors
from triggered scripts.

This only affects the Trigger plugin; if you are not using Trigger, you are
not affected by this flaw.  As a workaround, you can disable Trigger until you
are able to upgrade."

In Debian (and all other distros I know of) the bcfg2 server runs as
root, so in practice this is a remote root hole (limited to attackers
who can connect to the bcfg2 server (protected by a password and/or an
ssl key)).

-- 
Arto Jantunen

commit 8b0a5c5fc3ca99f6a2a8c393cedd02be66e6a846 (HEAD, squeeze-security)
Author: Arto Jantunen <vi...@debian.org>
Date:   Wed Jun 27 12:00:08 2012 +0300

    Backport upstream patch to fix unescaped shell command issues in the Trigger plugin

diff --git a/debian/patches/0005-Fix-unescaped-shell-commands-in-the-Trigger-plugin.patch b/debian/patches/0005-Fix-unescaped-shell-commands-in-the-Trigger-plugin.patch
new file mode 100644
index 0000000..fd58e79
--- /dev/null
+++ b/debian/patches/0005-Fix-unescaped-shell-commands-in-the-Trigger-plugin.patch
@@ -0,0 +1,69 @@
+From: Chris St. Pierre <chris.a.st.pie...@gmail.com>
+Date: Tue, 12 Jun 2012 09:20:10 -0400
+Subject: [PATCH] Fix unescaped shell commands in the Trigger plugin
+
+---
+ src/lib/Server/Plugins/Trigger.py |   42 ++++++++++++++++++++++++------------
+ 1 files changed, 28 insertions(+), 14 deletions(-)
+
+diff --git a/src/lib/Server/Plugins/Trigger.py b/src/lib/Server/Plugins/Trigger.py
+index b457431..5e6007e 100644
+--- a/src/lib/Server/Plugins/Trigger.py
++++ b/src/lib/Server/Plugins/Trigger.py
+@@ -1,17 +1,7 @@
+ import os
++import pipes
+ import Bcfg2.Server.Plugin
+-
+-
+-def async_run(prog, args):
+-    pid = os.fork()
+-    if pid:
+-        os.waitpid(pid, 0)
+-    else:
+-        dpid = os.fork()
+-        if not dpid:
+-            os.system(" ".join([prog] + args))
+-        os._exit(0)
+-
++from subprocess import Popen, PIPE
+ 
+ class Trigger(Bcfg2.Server.Plugin.Plugin,
+               Bcfg2.Server.Plugin.Statistics):
+@@ -27,9 +17,33 @@ class Trigger(Bcfg2.Server.Plugin.Plugin,
+             self.logger.error("Trigger: spool directory %s does not exist; unloading" % self.data)
+             raise Bcfg2.Server.Plugin.PluginInitError
+ 
++    def async_run(self, args):
++        pid = os.fork()
++        if pid:
++            os.waitpid(pid, 0)
++        else:
++            dpid = os.fork()
++            if not dpid:
++                self.debug_log("Running %s" % " ".join(pipes.quote(a)
++                                                       for a in args))
++                proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
++                (out, err) = proc.communicate()
++                rv = proc.wait()
++                if rv != 0:
++                    self.logger.error("Trigger: Error running %s (%s): %s" %
++                                      (args[0], rv, err))
++                elif err:
++                    self.debug_log("Trigger: Error: %s" % err)
++            os._exit(0)
++
+     def process_statistics(self, metadata, _):
+         args = [metadata.hostname, '-p', metadata.profile, '-g',
+                 ':'.join([g for g in metadata.groups])]
+         for notifier in os.listdir(self.data):
+-            n = self.data + '/' + notifier
+-            async_run(n, args)
++            if ((notifier[-1] == '~') or
++                (notifier[:2] == '.#') or
++                (notifier[-4:] == '.swp') or
++                (notifier in ['SCCS', '.svn', '4913'])):
++                continue
++            npath = os.path.join(self.data, notifier)
++            self.async_run([npath] + args)
+-- 
diff --git a/debian/patches/series b/debian/patches/series
index 4086f4e..6b4ca70 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -2,3 +2,4 @@
 0002-apt-deprecation-warnings.patch
 0003-agent-in-manpage.patch
 0004-unescaped-shell-command-fixes.patch
+0005-Fix-unescaped-shell-commands-in-the-Trigger-plugin.patch
\ No newline at end of file

Reply via email to