2eda5e
From 9a8a3488c465c036ca721695d17237e5c5fee25c Mon Sep 17 00:00:00 2001
2eda5e
From: Laurent Bigonville <bigon@bigon.be>
2eda5e
Date: Thu, 16 Jul 2015 01:06:06 +0200
2eda5e
Subject: [PATCH] Add SELinux support to run jobs in the proper domain
2eda5e
MIME-Version: 1.0
2eda5e
Content-Type: text/plain; charset=UTF-8
2eda5e
Content-Transfer-Encoding: 8bit
2eda5e
2eda5e
Currently, jobs run by at are run in the crond_t domain and not
2eda5e
transitioned outside of it.
2eda5e
2eda5e
With this patch, the jobs are transitioned in the same domain as the
2eda5e
jobs that are run by the cron daemon:
2eda5e
2eda5e
- When cron_userdomain_transition is set to off, a process for an
2eda5e
  unconfined user will transition to unconfined_cronjob_t. For confined
2eda5e
  user, the job is run as cronjob_t.
2eda5e
2eda5e
- When cron_userdomain_transition is set to on, the processes are run
2eda5e
  under the user default context.
2eda5e
2eda5e
This patch is based on Marcela Mašláňová <mmaslano@redhat.com> work
2eda5e
2eda5e
Signed-off-by: Jan Staněk <jstanek@redhat.com>
2eda5e
---
2eda5e
 Makefile.in  |  1 +
2eda5e
 atd.c        | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2eda5e
 config.h.in  |  3 ++
2eda5e
 configure.ac |  8 +++++
2eda5e
 daemon.c     | 16 ++++++++++
2eda5e
 daemon.h     |  3 ++
2eda5e
 6 files changed, 119 insertions(+)
2eda5e
2eda5e
diff --git a/Makefile.in b/Makefile.in
2eda5e
index 679bf0e..895f87f 100644
2eda5e
--- a/Makefile.in
2eda5e
+++ b/Makefile.in
2eda5e
@@ -39,6 +39,7 @@ LIBS		= @LIBS@
2eda5e
 LIBOBJS		= @LIBOBJS@
2eda5e
 INSTALL		= @INSTALL@
2eda5e
 PAMLIB          = @PAMLIB@
2eda5e
+SELINUXLIB      = @SELINUXLIB@
20eab7
 
2eda5e
 CLONES		= atq atrm
2eda5e
 ATOBJECTS	= at.o panic.o perm.o posixtm.o y.tab.o lex.yy.o
2eda5e
diff --git a/atd.c b/atd.c
2eda5e
index 63e514a..acb610f 100644
2eda5e
--- a/atd.c
2eda5e
+++ b/atd.c
2eda5e
@@ -86,6 +86,13 @@
2eda5e
 #ifndef LOG_ATD
2eda5e
 #define LOG_ATD        LOG_DAEMON
2eda5e
 #endif
2eda5e
+
20eab7
+#ifdef WITH_SELINUX
20eab7
+#include <selinux/selinux.h>
20eab7
+#include <selinux/get_context_list.h>
2eda5e
+int selinux_enabled = 0;
20eab7
+#endif
20eab7
+
2eda5e
 /* Macros */
2eda5e
 
2eda5e
 #define BATCH_INTERVAL_DEFAULT 60
2eda5e
@@ -202,6 +209,72 @@ myfork()
20eab7
 #define ATD_MAIL_NAME    "mailx"
20eab7
 #endif
20eab7
 
20eab7
+#ifdef WITH_SELINUX
2eda5e
+static int
2eda5e
+set_selinux_context(const char *name, const char *filename) {
2eda5e
+    security_context_t user_context = NULL;
2eda5e
+    security_context_t file_context = NULL;
2eda5e
+    int retval = 0;
2eda5e
+    char *seuser = NULL;
2eda5e
+    char *level = NULL;
2eda5e
+
2eda5e
+    if (getseuserbyname(name, &seuser, &level) == 0) {
2eda5e
+        retval = get_default_context_with_level(seuser, level, NULL, &user_context);
2eda5e
+        free(seuser);
2eda5e
+        free(level);
2eda5e
+        if (retval < 0) {
2eda5e
+            lerr("get_default_context_with_level: couldn't get security context for user %s", name);
2eda5e
+            retval = -1;
2eda5e
+            goto err;
2eda5e
+        }
2eda5e
+    }
2eda5e
+
2eda5e
+    /*
2eda5e
+     * Since crontab files are not directly executed,
2eda5e
+     * crond must ensure that the crontab file has
2eda5e
+     * a context that is appropriate for the context of
2eda5e
+     * the user cron job.  It performs an entrypoint
2eda5e
+     * permission check for this purpose.
2eda5e
+     */
2eda5e
+    if (fgetfilecon(STDIN_FILENO, &file_context) < 0) {
2eda5e
+        lerr("fgetfilecon FAILED %s", filename);
2eda5e
+        retval = -1;
2eda5e
+        goto err;
2eda5e
+    }
2eda5e
+
2eda5e
+    retval = selinux_check_access(user_context, file_context, "file", "entrypoint", NULL);
2eda5e
+    freecon(file_context);
2eda5e
+    if (retval < 0) {
2eda5e
+        lerr("Not allowed to set exec context to %s for user  %s", user_context, name);
2eda5e
+        retval = -1;
2eda5e
+        goto err;
2eda5e
+    }
2eda5e
+    if (setexeccon(user_context) < 0) {
2eda5e
+        lerr("Could not set exec context to %s for user  %s", user_context, name);
2eda5e
+        retval = -1;
2eda5e
+        goto err;
2eda5e
+    }
2eda5e
+err:
2eda5e
+    if (retval < 0 && security_getenforce() != 1)
2eda5e
+        retval = 0;
2eda5e
+    if (user_context)
2eda5e
+        freecon(user_context);
2eda5e
+    return retval;
2eda5e
+}
2eda5e
+
2eda5e
+static int
2eda5e
+selinux_log_callback (int type, const char *fmt, ...)
2eda5e
+{
2eda5e
+    va_list ap;
2eda5e
+
2eda5e
+    va_start(ap, fmt);
2eda5e
+    vsyslog (LOG_ERR, fmt, ap);
2eda5e
+    va_end(ap);
2eda5e
+    return 0;
20eab7
+}
2eda5e
+
20eab7
+#endif
20eab7
+
20eab7
 static void
20eab7
 run_file(const char *filename, uid_t uid, gid_t gid)
20eab7
 {
2eda5e
@@ -433,6 +506,13 @@ run_file(const char *filename, uid_t uid, gid_t gid)
2eda5e
 
2eda5e
 	    nice((tolower((int) queue) - 'a' + 1) * 2);
20eab7
 
20eab7
+#ifdef WITH_SELINUX
2eda5e
+	    if (selinux_enabled > 0) {
2eda5e
+	        if (set_selinux_context(pentry->pw_name, filename) < 0)
2eda5e
+	            perr("SELinux Failed to set context\n");
2eda5e
+	    }
20eab7
+#endif
2eda5e
+
2eda5e
 	    if (initgroups(pentry->pw_name, pentry->pw_gid))
2eda5e
 		perr("Cannot initialize the supplementary group access list");
2eda5e
 
2eda5e
@@ -751,6 +831,14 @@ main(int argc, char *argv[])
20eab7
     struct passwd *pwe;
20eab7
     struct group *ge;
20eab7
 
20eab7
+#ifdef WITH_SELINUX
20eab7
+    selinux_enabled=is_selinux_enabled();
2eda5e
+
2eda5e
+    if (selinux_enabled) {
2eda5e
+        selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) selinux_log_callback);
2eda5e
+    }
20eab7
+#endif
20eab7
+
20eab7
 /* We don't need root privileges all the time; running under uid and gid
20eab7
  * daemon is fine.
20eab7
  */
2eda5e
diff --git a/config.h.in b/config.h.in
2eda5e
index 16b2ed3..d1be135 100644
2eda5e
--- a/config.h.in
2eda5e
+++ b/config.h.in
2eda5e
@@ -192,6 +192,9 @@
2eda5e
    <sys/cpustats.h>. */
2eda5e
 #undef UMAX4_3
20eab7
 
2eda5e
+/* Define if you are building with_selinux */
20eab7
+#undef WITH_SELINUX
20eab7
+
2eda5e
 /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
2eda5e
    `char[]'. */
2eda5e
 #undef YYTEXT_POINTER
2eda5e
diff --git a/configure.ac b/configure.ac
2eda5e
index d5e16b3..85174d9 100644
2eda5e
--- a/configure.ac
2eda5e
+++ b/configure.ac
2eda5e
@@ -245,6 +245,14 @@ AC_DEFINE(WITH_PAM),
2eda5e
 AC_CHECK_LIB(pam, pam_start, PAMLIB='-lpam -lpam_misc')
2eda5e
 AC_SUBST(PAMLIB)
20eab7
 
20eab7
+AC_ARG_WITH(selinux,
20eab7
+[ --with-selinux       Define to run with selinux],
2eda5e
+AC_DEFINE(WITH_SELINUX, 1, [Define if you are building with_selinux]),
20eab7
+)
20eab7
+AC_CHECK_LIB(selinux, is_selinux_enabled, SELINUXLIB=-lselinux)
20eab7
+AC_SUBST(SELINUXLIB)
20eab7
+AC_SUBST(WITH_SELINUX)
20eab7
+
2eda5e
 AC_MSG_CHECKING(groupname to run under)
2eda5e
 AC_ARG_WITH(daemon_groupname,
2eda5e
 [ --with-daemon_groupname=DAEMON_GROUPNAME	Groupname to run under (default daemon) ],
2eda5e
diff --git a/daemon.c b/daemon.c
2eda5e
index 8be40d4..f9d25e7 100644
2eda5e
--- a/daemon.c
2eda5e
+++ b/daemon.c
2eda5e
@@ -82,6 +82,22 @@ perr(const char *fmt,...)
2eda5e
     exit(EXIT_FAILURE);
2eda5e
 }
2eda5e
 
2eda5e
+void
2eda5e
+lerr(const char *fmt,...)
2eda5e
+{
2eda5e
+    char buf[1024];
2eda5e
+    va_list args;
2eda5e
+
2eda5e
+    va_start(args, fmt);
2eda5e
+    vsnprintf(buf, sizeof(buf), fmt, args);
2eda5e
+    va_end(args);
2eda5e
+
2eda5e
+    if (daemon_debug) {
2eda5e
+	perror(buf);
2eda5e
+    } else
2eda5e
+	syslog(LOG_ERR, "%s: %m", buf);
2eda5e
+}
20eab7
+
2eda5e
 void
2eda5e
 pabort(const char *fmt,...)
2eda5e
 {
2eda5e
diff --git a/daemon.h b/daemon.h
2eda5e
index c4507ae..f44ccd1 100644
2eda5e
--- a/daemon.h
2eda5e
+++ b/daemon.h
2eda5e
@@ -13,5 +13,8 @@ __attribute__((noreturn))
2eda5e
 #endif
2eda5e
 perr (const char *fmt, ...);
20eab7
 
2eda5e
+void
2eda5e
+lerr (const char *fmt, ...);
2eda5e
+
2eda5e
 extern int daemon_debug;
2eda5e
 extern int daemon_foreground;
2eda5e
-- 
2eda5e
2.35.1
2eda5e