diff mbox

dpkg: run maintainer scripts with SELinux user system_u

Message ID CAJ2a_DcVJVogztwh7Wx88W8tDv0L4GmrsaCr-hYd1GND25w56Q@mail.gmail.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Christian Göttsche Jan. 25, 2017, 5:29 p.m. UTC
Hi list,
I created patch against dpkg, which is reported here:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=852539
Laurent suggested to post it also on this ML for discussion.


Currently, dpkg runs its maintainer tasks in the SELinux type
dpkg_script_t without changing the SELinux user or role.
So when running root as sysadm_u:sysadm_r:sysadm_t, the tasks will be
run in unconfined_u:unconfined_r:dpkg_script_t.
The problem are the postinst scripts: They create files and run binaries.
Almost all the files created in this way do not have the correct file
context system_u:object_r:*, which can break a ubac enabled system.
e.g.:

Would relabel /usr/share/info/dir.old from staff_u:object_r:usr_t:s0
to system_u:object_r:usr_t:s0
Would relabel /usr/share/info/dir from staff_u:object_r:usr_t:s0 to
system_u:object_r:usr_t:s0
Would relabel /var/cache/man/pt/index.db from
unconfined_u:object_r:man_cache_t:s0 to
system_u:object_r:man_cache_t:s0

Also, for example, the exim4 post install script does some work
leading to run exim in system_mail_t, which is not allowed to run
under the roles sysadm_r/unconfined_r.

type=PROCTITLE msg=audit(01/24/17 15:51:28.963:2602) :
proctitle=/usr/sbin/exim4 -C /var/lib/exim4/config.autogenerated.tmp
-bV
type=SYSCALL msg=audit(01/24/17 15:51:28.963:2602) : arch=armeb
syscall=socket per=PER_LINUX_32BIT success=yes exit=4 a0=local
a1=SOCK_STREAM a2=ip a3=0x0 items=0 ppid=22511 pid=22748
auid=christian uid=root gid=root euid=root suid=root fsuid=root
egid=root sgid=root fsgid=root tty=pts1 ses=359 comm=exim4
exe=/usr/sbin/exim4 subj=staff_u:sysadm_r:system_mail_t:s0 key=(null)
type=SELINUX_ERR msg=audit(01/24/17 15:51:28.963:2602) :
op=security_compute_sid
invalid_context=staff_u:sysadm_r:system_mail_t:s0
scontext=staff_u:sysadm_r:system_mail_t:s0
tcontext=staff_u:sysadm_r:system_mail_t:s0 tclass=unix_stream_socket

This can cause issues when upgrading packages in enforced mode even as
unconfined user.

The following dpkg patch runs the maintainer tasks in the context
system_u:system_r:dpkg_script_t (may be altered inside the SELinux
policy):

Note: The patch does not touch the SELinux detection in the build
logic and the SELinux policy has to be updated beforehand.

From: root <root@debianSE>
Date: Mon, 9 Jan 2017 22:42:03 +0100
Subject: [PATCH] dpkg: fix maintainer SELinux context

---
src/script.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 85 insertions(+), 10 deletions(-)




Best Regards,
       Christian Göttsche

Comments

Stephen Smalley Jan. 25, 2017, 6:18 p.m. UTC | #1
On Wed, 2017-01-25 at 18:29 +0100, cgzones wrote:
> Hi list,
> I created patch against dpkg, which is reported here:
> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=852539
> Laurent suggested to post it also on this ML for discussion.
> 
> 
> Currently, dpkg runs its maintainer tasks in the SELinux type
> dpkg_script_t without changing the SELinux user or role.
> So when running root as sysadm_u:sysadm_r:sysadm_t, the tasks will be
> run in unconfined_u:unconfined_r:dpkg_script_t.
> The problem are the postinst scripts: They create files and run
> binaries.
> Almost all the files created in this way do not have the correct file
> context system_u:object_r:*, which can break a ubac enabled system.
> e.g.:
> 
> Would relabel /usr/share/info/dir.old from staff_u:object_r:usr_t:s0
> to system_u:object_r:usr_t:s0
> Would relabel /usr/share/info/dir from staff_u:object_r:usr_t:s0 to
> system_u:object_r:usr_t:s0
> Would relabel /var/cache/man/pt/index.db from
> unconfined_u:object_r:man_cache_t:s0 to
> system_u:object_r:man_cache_t:s0
> 
> Also, for example, the exim4 post install script does some work
> leading to run exim in system_mail_t, which is not allowed to run
> under the roles sysadm_r/unconfined_r.
> 
> type=PROCTITLE msg=audit(01/24/17 15:51:28.963:2602) :
> proctitle=/usr/sbin/exim4 -C /var/lib/exim4/config.autogenerated.tmp
> -bV
> type=SYSCALL msg=audit(01/24/17 15:51:28.963:2602) : arch=armeb
> syscall=socket per=PER_LINUX_32BIT success=yes exit=4 a0=local
> a1=SOCK_STREAM a2=ip a3=0x0 items=0 ppid=22511 pid=22748
> auid=christian uid=root gid=root euid=root suid=root fsuid=root
> egid=root sgid=root fsgid=root tty=pts1 ses=359 comm=exim4
> exe=/usr/sbin/exim4 subj=staff_u:sysadm_r:system_mail_t:s0 key=(null)
> type=SELINUX_ERR msg=audit(01/24/17 15:51:28.963:2602) :
> op=security_compute_sid
> invalid_context=staff_u:sysadm_r:system_mail_t:s0
> scontext=staff_u:sysadm_r:system_mail_t:s0
> tcontext=staff_u:sysadm_r:system_mail_t:s0 tclass=unix_stream_socket
> 
> This can cause issues when upgrading packages in enforced mode even
> as
> unconfined user.
> 
> The following dpkg patch runs the maintainer tasks in the context
> system_u:system_r:dpkg_script_t (may be altered inside the SELinux
> policy):

This only supports running all maintainer tasks in a single, fixed
security context that is not based in any way on the context in which
dpkg itself runs.  Whereas the setexecfilecon() approach allows for
different scripts to run in different roles or domains via policy
transitions, and for the user identity and range to be preserved from
the dpkg instance.  I understand the motivation but not sure this is a
general approach.  I'm not clear anymore on why ubac even exists, since
if you truly want that type of separation, you are better off using
roles given that modern SELinux supports fine-grained role transitions
and roles on files (originally UBAC was a fallback I believe due to the
lack of support for roles on files).

> 
> Note: The patch does not touch the SELinux detection in the build
> logic and the SELinux policy has to be updated beforehand.
> 
> From: root <root@debianSE>
> Date: Mon, 9 Jan 2017 22:42:03 +0100
> Subject: [PATCH] dpkg: fix maintainer SELinux context
> 
> ---
> src/script.c | 95
> +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 85 insertions(+), 10 deletions(-)
> 
> diff --git a/src/script.c b/src/script.c
> index 2f252ae..72b92cf 100644
> --- a/src/script.c
> +++ b/src/script.c
> @@ -32,6 +32,7 @@
> #include <stdlib.h>
> 
> #ifdef WITH_LIBSELINUX
> +#include <ctype.h> // isspace
> #include <selinux/selinux.h>
> #endif
> 
> @@ -141,23 +142,97 @@ maintscript_pre_exec(struct command *cmd)
>        return cmd->filename + instdirlen;
> }
> 
> +#ifdef WITH_LIBSELINUX
> +/*
> + * derived from get_init_context()
> + * https://github.com/SELinuxProject/selinux/blob/master/policycoreu
> tils/run_init/run_init.c
> + *
> + * Get the CONTEXT associated with the context for the dpkg maint
> scripts.
> + *
> + * in:          nothing
> + * out:         The CONTEXT associated with the context.
> + * return:      0 on success, -1 on failure.
> + */
> +static int
> +get_dpkg_context(char **context)
> +{
> +       FILE *fp;
> +       char buf[255], *bufp;
> +       size_t buf_len;
> +       char context_file[4096];
> +       snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
> selinux_contexts_path(), "dpkg_context");
> +       fp = fopen(context_file, "r");
> +       if (!fp) {
> +               ohshite(_("Could not open file %s\n"), context_file);
> +               return -1;
> +       }
> +
> +       while (1) {             /* loop until we find a non-empty
> line */
> +
> +               if (!fgets(buf, sizeof buf, fp)) {
> +                       break;
> +               }
> +
> +               buf_len = strlen(buf);
> +               if (buf[buf_len - 1] == '\n') {
> +                        buf[buf_len - 1] = 0;
> +               }
> +
> +               bufp = buf;
> +               while (*bufp && isspace(*bufp)) {
> +                        bufp++;
> +               }
> +
> +               if (*bufp) {
> +                       *context = strdup(bufp);
> +                       if (!(*context)) {
> +                               goto out;
> +                       }
> +                       fclose(fp);
> +                       return 0;
> +               }
> +       }
> +      out:
> +       fclose(fp);
> +       ohshit(_("No context in file %s\n"), context_file);
> +       return -1;
> +}
> +#endif
> +
> /**
>  * Set a new security execution context for the maintainer script.
> - *
> - * Try to create a new execution context based on the current one
> and the
> - * specific maintainer script filename. If it's the same as the
> current
> - * one, use the given fallback.
>  */
> static int
> -maintscript_set_exec_context(struct command *cmd, const char
> *fallback)
> +maintscript_set_exec_context(void)
> {
> +#ifdef WITH_LIBSELINUX
>        int rc = 0;
> +       char *dpkg_context = NULL;
> 
> -#ifdef WITH_LIBSELINUX
> -       rc = setexecfilecon(cmd->filename, fallback);
> -#endif
> +       if (is_selinux_enabled() < 1) {
> +               return 0;
> +       }
> 
> -       return rc < 0 ? rc : 0;
> +       if ((rc = get_dpkg_context(&dpkg_context)) < 0) {
> +               ohshit(_("Can not get dpkg_context"));
> +               goto out;
> +       }
> +
> +       if ((rc = setexeccon(dpkg_context)) < 0) {
> +               ohshite(_("Can not set exec content to %s"),
> dpkg_context);
> +               goto out;;
> +       }
> +
> +      out:
> +       if (rc < 0 && security_getenforce() == 0) {
> +               rc = 0;
> +       }
> +
> +       free(dpkg_context);
> +       return rc;
> +#else
> +       return 0;
> +#endif
> }
> 
> static int
> @@ -190,7 +265,7 @@ maintscript_exec(struct pkginfo *pkg, struct
> pkgbin *pkgbin,
> 
>                cmd->filename = cmd->argv[0] =
> maintscript_pre_exec(cmd);
> 
> -               if (maintscript_set_exec_context(cmd,
> "dpkg_script_t") < 0)
> +               if (maintscript_set_exec_context() < 0)
>                        ohshite(_("cannot set security execution
> context for "
>                                  "maintainer script"));
> 
> --
> 2.11.0
> 
> 
> The policy patch would look like this:
> 
> From: cgzones <cgzones@googlemail.com>
> Date: Tue, 10 Jan 2017 14:19:54 +0100
> Subject: add dpkg_context appcontext
> 
> ---
>  Makefile                               | 2 +-
>  config/appconfig-mcs/dpkg_context      | 1 +
>  config/appconfig-mls/dpkg_context      | 1 +
>  config/appconfig-standard/dpkg_context | 1 +
>  4 files changed, 4 insertions(+), 1 deletion(-)
>  create mode 100644 config/appconfig-mcs/dpkg_context
>  create mode 100644 config/appconfig-mls/dpkg_context
>  create mode 100644 config/appconfig-standard/dpkg_context
> 
> diff --git a/Makefile b/Makefile
> index 154beb57c..1ad9e079f 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -250,7 +250,7 @@ seusers := $(appconf)/seusers
>  appdir := $(contextpath)
>  user_default_contexts := $(wildcard
> config/appconfig-$(TYPE)/*_default_contexts)
>  user_default_contexts_names := $(addprefix
> $(contextpath)/users/,$(subst _default_contexts,,$(notdir
> $(user_default_contexts))))
> -appfiles := $(addprefix $(appdir)/,default_contexts default_type
> initrc_context failsafe_context userhelper_context removable_context
> dbus_contexts sepgsql_contexts x_contexts customizable_types
> securetty_types lxc_contexts virtual_domain_context
> virtual_image_context) $(contextpath)/files/media $(fcsubspath)
> $(user_default_contexts_names)
> +appfiles := $(addprefix $(appdir)/,default_contexts default_type
> initrc_context dpkg_context failsafe_context userhelper_context
> removable_context dbus_contexts sepgsql_contexts x_contexts
> customizable_types securetty_types lxc_contexts
> virtual_domain_context
> virtual_image_context) $(contextpath)/files/media $(fcsubspath)
> $(user_default_contexts_names)
>  net_contexts := $(builddir)net_contexts
> 
>  all_layers := $(shell find $(wildcard $(moddir)/*) -maxdepth 0 -type
> d)
> diff --git a/config/appconfig-mcs/dpkg_context
> b/config/appconfig-mcs/dpkg_context
> new file mode 100644
> index 000000000..f7ed03c8c
> --- /dev/null
> +++ b/config/appconfig-mcs/dpkg_context
> @@ -0,0 +1 @@
> +system_u:system_r:dpkg_script_t:s0
> diff --git a/config/appconfig-mls/dpkg_context
> b/config/appconfig-mls/dpkg_context
> new file mode 100644
> index 000000000..555917148
> --- /dev/null
> +++ b/config/appconfig-mls/dpkg_context
> @@ -0,0 +1 @@
> +system_u:system_r:dpkg_script_t:s0-mls_systemhigh
> diff --git a/config/appconfig-standard/dpkg_context
> b/config/appconfig-standard/dpkg_context
> new file mode 100644
> index 000000000..7c56bf1f5
> --- /dev/null
> +++ b/config/appconfig-standard/dpkg_context
> @@ -0,0 +1 @@
> +system_u:system_r:dpkg_script_t
> 
> 
> 
> Best Regards,
>        Christian Göttsche
> 
> _______________________________________________
> Selinux mailing list
> Selinux@tycho.nsa.gov
> To unsubscribe, send email to Selinux-leave@tycho.nsa.gov.
> To get help, send an email containing "help" to Selinux-request@tycho
> .nsa.gov.
diff mbox

Patch

diff --git a/src/script.c b/src/script.c
index 2f252ae..72b92cf 100644
--- a/src/script.c
+++ b/src/script.c
@@ -32,6 +32,7 @@ 
#include <stdlib.h>

#ifdef WITH_LIBSELINUX
+#include <ctype.h> // isspace
#include <selinux/selinux.h>
#endif

@@ -141,23 +142,97 @@  maintscript_pre_exec(struct command *cmd)
       return cmd->filename + instdirlen;
}

+#ifdef WITH_LIBSELINUX
+/*
+ * derived from get_init_context()
+ * https://github.com/SELinuxProject/selinux/blob/master/policycoreutils/run_init/run_init.c
+ *
+ * Get the CONTEXT associated with the context for the dpkg maint scripts.
+ *
+ * in:          nothing
+ * out:         The CONTEXT associated with the context.
+ * return:      0 on success, -1 on failure.
+ */
+static int
+get_dpkg_context(char **context)
+{
+       FILE *fp;
+       char buf[255], *bufp;
+       size_t buf_len;
+       char context_file[4096];
+       snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
selinux_contexts_path(), "dpkg_context");
+       fp = fopen(context_file, "r");
+       if (!fp) {
+               ohshite(_("Could not open file %s\n"), context_file);
+               return -1;
+       }
+
+       while (1) {             /* loop until we find a non-empty line */
+
+               if (!fgets(buf, sizeof buf, fp)) {
+                       break;
+               }
+
+               buf_len = strlen(buf);
+               if (buf[buf_len - 1] == '\n') {
+                        buf[buf_len - 1] = 0;
+               }
+
+               bufp = buf;
+               while (*bufp && isspace(*bufp)) {
+                        bufp++;
+               }
+
+               if (*bufp) {
+                       *context = strdup(bufp);
+                       if (!(*context)) {
+                               goto out;
+                       }
+                       fclose(fp);
+                       return 0;
+               }
+       }
+      out:
+       fclose(fp);
+       ohshit(_("No context in file %s\n"), context_file);
+       return -1;
+}
+#endif
+
/**
 * Set a new security execution context for the maintainer script.
- *
- * Try to create a new execution context based on the current one and the
- * specific maintainer script filename. If it's the same as the current
- * one, use the given fallback.
 */
static int
-maintscript_set_exec_context(struct command *cmd, const char *fallback)
+maintscript_set_exec_context(void)
{
+#ifdef WITH_LIBSELINUX
       int rc = 0;
+       char *dpkg_context = NULL;

-#ifdef WITH_LIBSELINUX
-       rc = setexecfilecon(cmd->filename, fallback);
-#endif
+       if (is_selinux_enabled() < 1) {
+               return 0;
+       }

-       return rc < 0 ? rc : 0;
+       if ((rc = get_dpkg_context(&dpkg_context)) < 0) {
+               ohshit(_("Can not get dpkg_context"));
+               goto out;
+       }
+
+       if ((rc = setexeccon(dpkg_context)) < 0) {
+               ohshite(_("Can not set exec content to %s"), dpkg_context);
+               goto out;;
+       }
+
+      out:
+       if (rc < 0 && security_getenforce() == 0) {
+               rc = 0;
+       }
+
+       free(dpkg_context);
+       return rc;
+#else
+       return 0;
+#endif
}

static int
@@ -190,7 +265,7 @@  maintscript_exec(struct pkginfo *pkg, struct pkgbin *pkgbin,

               cmd->filename = cmd->argv[0] = maintscript_pre_exec(cmd);

-               if (maintscript_set_exec_context(cmd, "dpkg_script_t") < 0)
+               if (maintscript_set_exec_context() < 0)
                       ohshite(_("cannot set security execution context for "
                                 "maintainer script"));

--
2.11.0


The policy patch would look like this:

From: cgzones <cgzones@googlemail.com>
Date: Tue, 10 Jan 2017 14:19:54 +0100
Subject: add dpkg_context appcontext

---
 Makefile                               | 2 +-
 config/appconfig-mcs/dpkg_context      | 1 +
 config/appconfig-mls/dpkg_context      | 1 +
 config/appconfig-standard/dpkg_context | 1 +
 4 files changed, 4 insertions(+), 1 deletion(-)
 create mode 100644 config/appconfig-mcs/dpkg_context
 create mode 100644 config/appconfig-mls/dpkg_context
 create mode 100644 config/appconfig-standard/dpkg_context

diff --git a/Makefile b/Makefile
index 154beb57c..1ad9e079f 100644
--- a/Makefile
+++ b/Makefile
@@ -250,7 +250,7 @@  seusers := $(appconf)/seusers
 appdir := $(contextpath)
 user_default_contexts := $(wildcard
config/appconfig-$(TYPE)/*_default_contexts)
 user_default_contexts_names := $(addprefix
$(contextpath)/users/,$(subst _default_contexts,,$(notdir
$(user_default_contexts))))
-appfiles := $(addprefix $(appdir)/,default_contexts default_type
initrc_context failsafe_context userhelper_context removable_context
dbus_contexts sepgsql_contexts x_contexts customizable_types
securetty_types lxc_contexts virtual_domain_context
virtual_image_context) $(contextpath)/files/media $(fcsubspath)
$(user_default_contexts_names)
+appfiles := $(addprefix $(appdir)/,default_contexts default_type
initrc_context dpkg_context failsafe_context userhelper_context
removable_context dbus_contexts sepgsql_contexts x_contexts
customizable_types securetty_types lxc_contexts virtual_domain_context
virtual_image_context) $(contextpath)/files/media $(fcsubspath)
$(user_default_contexts_names)
 net_contexts := $(builddir)net_contexts

 all_layers := $(shell find $(wildcard $(moddir)/*) -maxdepth 0 -type d)
diff --git a/config/appconfig-mcs/dpkg_context
b/config/appconfig-mcs/dpkg_context
new file mode 100644
index 000000000..f7ed03c8c
--- /dev/null
+++ b/config/appconfig-mcs/dpkg_context
@@ -0,0 +1 @@ 
+system_u:system_r:dpkg_script_t:s0
diff --git a/config/appconfig-mls/dpkg_context
b/config/appconfig-mls/dpkg_context
new file mode 100644
index 000000000..555917148
--- /dev/null
+++ b/config/appconfig-mls/dpkg_context
@@ -0,0 +1 @@ 
+system_u:system_r:dpkg_script_t:s0-mls_systemhigh
diff --git a/config/appconfig-standard/dpkg_context
b/config/appconfig-standard/dpkg_context
new file mode 100644
index 000000000..7c56bf1f5
--- /dev/null
+++ b/config/appconfig-standard/dpkg_context
@@ -0,0 +1 @@ 
+system_u:system_r:dpkg_script_t